From 07d37e2edbaf75eae48ddef3356991a3bf27f2f1 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Wed, 27 Aug 2025 10:47:33 +0200 Subject: [PATCH 1/2] Allow limited HTML in session descriptions --- scripts/sync-sched/schedule-2025.json | 102 +++++----- scripts/sync-sched/speakers.json | 184 ++++++++++-------- scripts/sync-sched/sync.ts | 1 + .../2025/schedule/[id]/format-description.tsx | 66 ++++--- src/app/conf/2025/schedule/[id]/page.tsx | 28 ++- .../_components/schedule-session-card.tsx | 13 +- .../2025/speakers/[id]/long-session-card.tsx | 3 +- src/app/conf/_api/sched-client.tsx | 15 +- src/app/conf/_api/sched-types.ts | 3 + 9 files changed, 232 insertions(+), 183 deletions(-) diff --git a/scripts/sync-sched/schedule-2025.json b/scripts/sync-sched/schedule-2025.json index f2b1c9fc5e..71543f8b52 100644 --- a/scripts/sync-sched/schedule-2025.json +++ b/scripts/sync-sched/schedule-2025.json @@ -7,7 +7,7 @@ "event_start": "2025-09-07 16:00", "event_end": "2025-09-07 17:00", "event_type": "Breaks / Networking / Special Events", - "description": "GraphQLConf Canal Cruise - Separate Registration Required\n\nJoin us on Sunday 7 September for this pre GraphQLConf'25 event, a must-do activity in Amsterdam; a wonderful canal cruise in the historical city centre of Amsterdam.\n\nInitiative by the local Amsterdam GraphQL Meetup group.\n\nA great opportunity to meet your peer-attendees of the GraphQLConf'25 during this great sight-seeing activity.\n\nPlease RSVP by Wednesday September 3rd!\n\nLocation: in the city centre (tba exact location)\nDuration of the canal cruise: 16:00-17:00 on Sunday 7 September\nPlease be 15:45 at the location (to buy the ticket etc).\n\nCost per person: 10-15 euro* (depending on the number of attendees)\n*not included in the conference ticket", + "description": "GraphQLConf Canal Cruise - Separate Registration Required\n\nJoin us on Sunday 7 September for this pre GraphQLConf'25 event, a must-do activity in Amsterdam; a wonderful canal cruise in the historical city centre of Amsterdam.\n\nInitiative by the local Amsterdam GraphQL Meetup group.\n\nA great opportunity to meet your peer-attendees of the GraphQLConf'25 during this great sight-seeing activity.\n\nPlease RSVP by Wednesday September 3rd!\n\nLocation: in the city centre (tba exact location)\nDuration of the canal cruise: 16:00-17:00 on Sunday 7 September\nPlease be 15:45 at the location (to buy the ticket etc).\n\nCost per person: 10-15 euro* (depending on the number of attendees)\n*not included in the conference ticket", "goers": "0", "seats": "0", "invite_only": "N", @@ -41,7 +41,7 @@ "event_start": "2025-09-08 08:00", "event_end": "2025-09-08 19:15", "event_type": "Breaks / Networking / Special Events", - "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", + "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", "goers": "0", "seats": "0", "invite_only": "N", @@ -77,7 +77,7 @@ "event_start": "2025-09-08 08:00", "event_end": "2025-09-08 18:30", "event_type": "Registration + Badge Pick-up", - "goers": "3", + "goers": "5", "seats": "0", "invite_only": "N", "venue": "BG Foyer - Ground Floor", @@ -113,7 +113,7 @@ "event_start": "2025-09-08 09:00", "event_end": "2025-09-08 09:05", "event_type": "Keynote Sessions", - "goers": "4", + "goers": "6", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -159,7 +159,7 @@ "event_start": "2025-09-08 09:05", "event_end": "2025-09-08 09:15", "event_type": "Keynote Sessions", - "goers": "5", + "goers": "7", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -206,7 +206,7 @@ "event_end": "2025-09-08 09:30", "event_type": "Keynote Sessions", "description": "Even ten years in, GraphQL continues to evolve—not just in code, but in connection. This year the Foundation has doubled down on transparency, support, and shared leadership: board minutes are now public, Subject Matter Experts have helped shape the conference agenda, and we'll be launching a new program live on stage! There are also updates on our existing initiatives including community grants and GraphQL Locals.\n\nThis talk is a thank you to the people behind the progress and a celebration of our growing constellation of contributors. It's also an invitation to step forward and get involved—one of the best ways to do that is by joining our new Community Working Group, giving passionate community members a voice in shaping the Foundation's directions and initiatives for the next ten years of GraphQL.", - "goers": "5", + "goers": "7", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -266,7 +266,7 @@ "event_end": "2025-09-08 09:45", "event_type": "Keynote Sessions", "description": "Meet the new developer journey: Ask AI → Generate code → Iterate → Ship. This fundamental shift in how developers work demands we rethink every touchpoint of our GraphQL APIs. This talk focuses on the developer experience layer—how to design schemas that are self-explanatory, structure documentation so AI gives accurate answers about your API, and build tools that feel like pair programming with a senior engineer.", - "goers": "3", + "goers": "5", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -311,7 +311,7 @@ "event_end": "2025-09-08 10:00", "event_type": "Keynote Sessions", "description": "A peek behind the curtain revealing how GraphQL is used at Meta. We will explore how everything from culture, development process, client and server implementations, schema patterns and conventions, advanced tooling and more work together to allow GraphQL to enable great user and developer experiences at Meta.", - "goers": "4", + "goers": "6", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -357,7 +357,7 @@ "event_end": "2025-09-08 10:15", "event_type": "Keynote Sessions", "description": "As developers build with AI agents, we face a challenge: how do we provide these agents with reliable, flexible access to our distributed data? GraphQL's graph-based approach makes it the ideal language for AI. Join Matt DeBergalis, CEO and Co-founder of Apollo GraphQL, to explore how \"thinking in graphs\" fundamentally transforms API orchestration from procedural code to declarative queries – creating the composable data layer that AI-driven applications require.", - "goers": "4", + "goers": "6", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -402,7 +402,7 @@ "event_start": "2025-09-08 10:15", "event_end": "2025-09-08 10:20", "event_type": "Keynote Sessions", - "goers": "4", + "goers": "6", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -448,7 +448,7 @@ "event_start": "2025-09-08 10:20", "event_end": "2025-09-08 10:45", "event_type": "Breaks / Networking / Special Events", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Foyer Grote Zaal - 2nd Floor", @@ -567,7 +567,7 @@ "event_end": "2025-09-08 11:15", "event_type": "GraphQL in Production", "description": "The most natural way to understand fragments is as a reusable part of a query. We at Meta know that this isn't true and can lead to a world of pain when it comes to making sure the data you fetch matches the code that uses that data (no over-fetching).\n\nThe worst part is both the GraphQL spec and the educational materials mention re-use for fragments as part of their value:\n\"Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document.\"\n\nThis talk will explain what we've learned is the best way to use fragments (as subcomponents you convert to in order to pass to the logic that is tied to that fragment).\n\nWe will use Relay's per-file graphql co-location as a demonstration of this philosophy in action", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -780,7 +780,7 @@ "event_end": "2025-09-08 11:55", "event_type": "GraphQL in Production", "description": "After maintaining GraphQL Java for 10 years we learned what aspects of GraphQL are critical for optimal performance. \n \nWe we look at what users of GraphQL should consider and which aspects of the spec are critical for optimal performance.\n \nWe will look also closer into the aspects of operating GraphQL at scale including and what it means when the schema and requests continue to grow.", - "goers": "1", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -872,7 +872,7 @@ "event_end": "2025-09-08 12:35", "event_type": "Developer Experience", "description": "GraphQL error handling sucks. There, I said it.\n\nEver hunted through the errors list to figure out if a null was legit or caused by an error? If you're like me, you gave up and now treat nulls as \"maybe errored, maybe absent, maybe both.\"\n\nAnd nullability. Schema designers make anything that might fail nullable, producing partial responses when errors occur. But since anything can fail, now everything is nullable—\nand we're drowning in null checks. We recklessly cast to non-null or fall back to the empty string out of desperation. And we still don't know what's truly nullable.\n\nNo more.\n\nThis talk introduces a new, pragmatic approach, born from years of work by the Nullability WG. We propose a future where schemas reflect the true nullability of business entities, and error handling is where it belongs: in your code, not your data. Use your language's built-in tools to handle errors ergonomically; and drop the unnecessary null checks. When you read a null, it should mean one thing: the absence of data.\n\nThis isn't some distant ideal on the horizon of GraphQL's future; with just 512 bytes added to your GraphQL client, you can start adopting this today. Come see how.", - "goers": "3", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -918,7 +918,7 @@ "event_end": "2025-09-08 12:35", "event_type": "GraphQL in Production", "description": "Abstract\nDiscover how we transformed Jira's issue view to GraphQL and Relay—handling billions of monthly interactions across 100+ field types while meeting enterprise compliance and reliability agreements. With hundreds of developers and teams modifying a decade-old codebase daily in a frontend monorepo, we faced unique challenges in technical design and execution.\nWe'll share:\n- GraphQL schema design for scale\n- Bridging Redux, Sweet State, and Relay in a multi-team environment\n- Incremental rollout strategies with feature flags for safe migration\n- Field-by-field adoption approaches maintaining workflow and compliance\n- Performance optimization under enterprise-scale load\n- Testing approaches at scale\n- Developer experience takeaways\n\nImpact & Takeaways\n- Performance metrics during incremental migration\n- Developer experience improvements\n- Cross-team collaboration\n- Production-proven strategies for state management in existing codebase\n\nThis isn't about building new—it's about modernizing Jira's critical interface while maintaining compliance for enterprise customers. Ideal for leaders coordinating teams and architects planning GraphQL adoption in regulated organizations.", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -971,7 +971,7 @@ "event_end": "2025-09-08 12:35", "event_type": "GraphQL in Production", "description": "@defer allows you to specify which parts of your operation are urgent, and which can be delayed. However, there is still a contract with @defer: all your data will always be returned, at some later point.\n\nThis poses a problem for certain classes of product: if only 10% of your operation will ever be on consumed, but you don't know exactly which 10% that will be, defer can introduce substantial hidden costs. To improve performance and reduce compute costs, Meta created @async to ensure products only ask for data when it's needed.", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -1016,7 +1016,7 @@ "event_start": "2025-09-08 12:35", "event_end": "2025-09-08 13:45", "event_type": "Breaks / Networking / Special Events", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Foyer Grote Zaal - 2nd Floor", @@ -1152,7 +1152,7 @@ "event_end": "2025-09-08 14:15", "event_type": "GraphQL in Production", "description": "GraphQL Subscriptions over WebSockets is an extremely popular way to notify the caller that something happened on the back end. While a high level of Quality of Service is possible in other protocols such as STOMP or MQTT, there is no actual guarantee of delivery with GraphQL Subscriptions: any acknowledgment mechanism should be built in userland.\n \nWebSockets can be interrupted at any time with both graceful and ungraceful disconnections, and the lack of acknowledgment in the subscription protocol affects reliability, making GraphQL Subscription over WebSocket connections susceptible to disconnections, as any protocol failures will result in messages being lost. In other words, Subscriptions are stateful. In this talk, we will investigate a mitigation technique to make them resumable so that the connection can be destroyed and recreated without any message loss.", - "goers": "0", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -1198,7 +1198,7 @@ "event_end": "2025-09-08 14:55", "event_type": "Developer Experience", "description": "GraphQL federation has changed how we think about data and it's time to adopt that thinking to how we build UIs and component systems. In this talk, Gabe will share his deep experience from building design systems at Apple, Netflix and now StubHub to help you design yours.\n \nWhat are the advantages GraphQL brings to a traditional React component system? How can fragments optimize the composability of your components? How to reduce duplication while reducing the time you need to ship new features?\n \nWe will provide guidance on how to build and leverage a federated component system along-side your design system. \n \nFinally, we will cover how we've leveraged AI to speedup the creation of our design system and federated components at StuHub. Join to learn more!", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -1244,7 +1244,7 @@ "event_end": "2025-09-08 14:55", "event_type": "GraphQL in Production", "description": "To ease life for the roughly 1,000 developers who contribute regularly to our GraphQL interface, Airbnb has a highly opinionated developer API that we believe eases life for both developers and the operators who maintain the service. In the past, this API was implemented on top of a traditional, specification-based GraphQL engine, which supported agility as we evolved our opinionated approach to resolvers. As that approach matured, we saw opportunities to build a more efficient engine to support them.\n \nThis talk describes our new GraphQL engine. Key elements in our design include:\n \n- Refactoring query execution into distinct resolution and completion phases.\n \n- A query planner that optimizes and orchestrates the execution of those phases.\n \n- A new data structure that is the intermediary between the two phases, a data structure that allows for principled communication between the many resolvers involved in executing a query", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -1389,7 +1389,7 @@ "event_end": "2025-09-08 15:35", "event_type": "GraphQL in Production", "description": "The Member Experience Core Systems team at Netflix is entrusted with orchestrating every facet of the member experience. From the intricacies of the profiles screen to the dynamic homepage and the seamless search for your favorite shows, our robust API layer is the backbone that supports it all.\n\nOperating at an astronomical scale, our two principal Subgraph Services collectively manage over a million GraphQL queries per second. This immense scale, coupled with the diverse queries our systems accommodate, ensures that even the most minute edge cases are brought to light.\n\nIn this presentation, we will delve into two significant production bugs that emerged at scale. The first involved a federation-based solution at the query planning layer, which culminated in a 20% reduction in requests per second and yielded substantial cost savings amounting to hundreds of thousands of dollars. The second, a subgraph service specific enhancement, remarkably doubled the efficiency of our entire fleet.\n\nBy sharing our journey of identifying and resolving these issues, we aspire to provide insights that will directly enhance your day-to-day endeavors using GraphQL.", - "goers": "1", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -1480,7 +1480,7 @@ "event_start": "2025-09-08 15:35", "event_end": "2025-09-08 15:55", "event_type": "Breaks / Networking / Special Events", - "goers": "1", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "Foyer Grote Zaal - 2nd Floor", @@ -1609,7 +1609,7 @@ "event_end": "2025-09-08 16:25", "event_type": "GraphQL in Production", "description": "Turning a private GraphQL API into a public one comes with unexpected challenges. We’ll share how we approached this transition—starting from an existing internal schema that wasn’t shaped for external consumers—and the steps we took to expose only what was ready. Using Apollo Federation Contracts, we filtered out unstable or sensitive parts of the graph. Along the way, we defined best practices for the public schema, like cursor-based pagination, using oneOf for inputs and results.\nWe’ll also touch on how we serve the schema through Hive Gateway with a supergraph setup, and the security measures we added, like depth limiting and complexity analysis. To keep things evolving safely, we rely on GraphQL Hive to track usage and guide deprecations.\n \nIf you’re thinking about exposing a GraphQL API—or just want ideas for keeping one clean and manageable—this talk will share what worked for us, what didn’t, and what we learned.", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -1747,7 +1747,7 @@ "event_end": "2025-09-08 17:05", "event_type": "GraphQL in Production", "description": "A talk covering the journey of The Guild building a query planner for the future. We are going to share our journey learnings, insights and real-life examples on how we built our open-source Federation query planner.\n\nIn this talk, we’ll cover:\n\n- The background and journey The Guild did (from composition to query planning)\n- An overview of our query planner architecture\n- Audit-based development\n- Why we build our query planner as a library\n- Next steps and the community can get involved", - "goers": "1", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -1800,7 +1800,7 @@ "event_end": "2025-09-08 17:05", "event_type": "GraphQL in Production", "description": "alt title: Surviving Change Without Breaking Everything\n \nWhen we launched our new GraphQL API at Netflix, it felt perfect—destined to power hundreds of millions of devices. Yet, change is inevitable. Even if your schema seems flawless today (which it isn't), requirements will shift, new features will emerge, and regrets will follow.\n \nGraphQL promises evolvability, allowing us to move forward without multiple API versions. But how does this hold up in practice? We mark fields as @deprecated, but what happens next? How can we embrace experimentation without entombing technical debt in the API? Does federation complicate things? Evolving your schema without breaking clients is easy, right? Right??\n \nDrawing from experience with the Netflix API, this talk explores techniques for evolving your schema safely and painlessly. We'll cover the schema lifecycle—from experimentation to design, deprecation, and deletion.\n \nAttendees will leave with:\n- Schema design principles that facilitate change\n- Practical techniques for evolving GraphQL schemas\n- Strategies for managing a deprecation workflow\n \nJoin us as we learn to face the inevitability of change with confidence and serenity.", - "goers": "3", + "goers": "5", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -1846,7 +1846,7 @@ "event_end": "2025-09-08 17:45", "event_type": "GraphQL in Production", "description": "Over the past six years, Viaduct has grown from Airbnb’s unified data access layer into a central platform for hosting business logic — now supporting over 1 million lines of code, 500+ monthly contributors, and 100+ teams.\n\nThat scale has brought a familiar risk: the slow creep toward monolith. Viaduct was never meant to be a microservices system, but we’ve had to make deliberate choices to preserve team autonomy, performance, and codebase sanity.\n\nThis talk shares the strategies we’re using — and actively evolving — to make that possible, including:\n\n* Tenant modules that define slices of the GraphQL schema alongside their implementation logic;\n* Relying on GraphQL fragments instead of service calls for inter-module communication;\n* Building ownership and attribution into the platform so teams can trace metrics and errors back to themselves.\n\nWe haven’t fully solved these challenges — but we’ve learned a lot about what works, what breaks, and what to watch for.", - "goers": "2", + "goers": "4", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -1938,7 +1938,7 @@ "event_end": "2025-09-08 17:45", "event_type": "GraphQL in Production", "description": "Relay, Meta’s advanced GraphQL client for React, has many innovative capabilities not available in other clients. We will explore these capabilities, what they are, how they work, and the fundamental problems they solve: \n \n- Ensure data consistency with a normalized cache \n- Bound memory usage with user-land garbage collection \n- Avoid append-only queries by statically detecting unused fields \n- Bound JavaScript bundle size with Data Driven Dependencies \n- Build snappy, robust, mutations with rebasing optimistic updates \n- Preload code and data for any surface in just one network roundtrip with Entrypoints \n- Enable optimal data fetching without sacrificing local reasoning with Relay’s compiler \n \nAnd more!", - "goers": "0", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -2019,7 +2019,7 @@ "event_start": "2025-09-09 08:00", "event_end": "2025-09-09 18:00", "event_type": "Breaks / Networking / Special Events", - "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", + "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", "goers": "0", "seats": "0", "invite_only": "N", @@ -2055,7 +2055,7 @@ "event_start": "2025-09-09 08:00", "event_end": "2025-09-09 17:30", "event_type": "Registration + Badge Pick-up", - "goers": "0", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "BG Foyer - Ground Floor", @@ -2163,7 +2163,7 @@ "event_start": "2025-09-09 09:00", "event_end": "2025-09-09 10:30", "event_type": "Workshops", - "description": "Join us for an intensive hands-on workshop where you'll build a complete e-commerce application stack, from GraphQL API to AI integration. We'll start by creating a robust GraphQL API using Apollo Router, covering schema design and federation best practices.\n\nThen we'll extend your GraphQL API with a Model Context Protocol (MCP) server, enabling structured AI data access and tool-based interactions. This allows AI assistants to query your products, manage inventory, and process orders directly through your API.This workshop emphasizes exceptional developer experience with hot-reloading capabilities across your entire stack for rapid iteration. You'll learn real-world patterns for connecting GraphQL APIs with MCP servers, handling data flow between layers, and maintaining type safety from your database to your AI tools.\n\nPrerequisites: Familiarity with GraphQL basics. Bring a laptop with Node.js installed.\n\nBy the end of this workshop, you'll be able to: Configure Apollo Router to orchestrate multiple GraphQL services into a unified graph Integrate microservices that extend and complement each other's capabilities Build and deploy a custom GraphQL API as part of your federated graph Expose any GraphQL operation as MCP tools with zero additional code, making your entire API instantly AI-accessible Understand practical use cases for AI-integrated applications in modern development workflows", + "description": "Join us for an intensive hands-on workshop where you'll build a complete e-commerce application stack, from GraphQL API to AI integration. We'll start by creating a robust GraphQL API using Apollo Router, covering schema design and federation best practices.\n\nThen we'll extend your GraphQL API with a Model Context Protocol (MCP) server, enabling structured AI data access and tool-based interactions. This allows AI assistants to query your products, manage inventory, and process orders directly through your API.This workshop emphasizes exceptional developer experience with hot-reloading capabilities across your entire stack for rapid iteration. You'll learn real-world patterns for connecting GraphQL APIs with MCP servers, handling data flow between layers, and maintaining type safety from your database to your AI tools.\n\nPrerequisites: Familiarity with GraphQL basics. Bring a laptop with Node.js installed.\n\nBy the end of this workshop, you'll be able to:", "goers": "0", "seats": "0", "invite_only": "N", @@ -2210,7 +2210,7 @@ "event_end": "2025-09-09 10:30", "event_type": "Workshops", "description": "This workshop proposes the development of a demo social media application, \"Y,\" using GraphQL, Relay, and React Server Components (RSC). The goal is to showcase the powerful synergy between Relay’s declarative data fetching and RSC’s server-side rendering, creating an ideal stack for scalable, performant web applications. \n \nThe demo will highlight: \n \n- Co-location of GraphQL fragments with React components for a modular, maintainable codebase. \n- Relay’s type-safe queries to efficiently fetch data for posts, comments, and user profiles. \n- React Server Components to optimize server-side rendering and enable React streaming for progressive UI rendering. \n- A fully functional social media app with interactive, real-time features. \n \nThrough this hands-on demo, we’ll illustrate how Relay’s fragment-based architecture and RSC’s streaming capabilities allow developers to build responsive, data-driven applications. This workshop will demonstrate the potential of these technologies for modern frontend development providing a compelling case for adopting this stack in production projects.", - "goers": "0", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -2255,7 +2255,7 @@ "event_start": "2025-09-09 10:30", "event_end": "2025-09-09 10:45", "event_type": "Breaks / Networking / Special Events", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Foyer Grote Zaal - 2nd Floor", @@ -2374,7 +2374,7 @@ "event_start": "2025-09-09 10:45", "event_end": "2025-09-09 12:15", "event_type": "Workshops", - "description": "In this workshop, we will look at different patterns to integrate various data sources in a federated graph using the Apollo Federation V2 spec, the Composite Schemas spec, WebAssembly with the recently released Component Model, and the Grafbase Gateway.\n\nGraphQL Federation is a fantastic pattern to combine APIs — called subgraphs — into a single schema while sharing types and declaratively depending from data from other subgraphs. In fact, it is too good to keep it to GraphQL subgraphs only — what if you could bring other kinds of APIs and data sources into your federated graph without having to write a dedicated GraphQL server in front of them?\n\nIn the course of the hands-on workshop, we will: Integrate a REST API, a Postgres database and a Kafka instance using open source extensions, Use the gRPC extension and the MCP server to understand, integrate and query gRPC services over GraphQL, Build a new custom extension together\nThe result will be a functioning GraphQL federated graph without GraphQL subgraphs, demonstrating that federation as a mechanism is useful beyond just GraphQL subgraphs.", + "description": "In this workshop, we will look at different patterns to integrate various data sources in a federated graph using the Apollo Federation V2 spec, the Composite Schemas spec, WebAssembly with the recently released Component Model, and the Grafbase Gateway.\n\nGraphQL Federation is a fantastic pattern to combine APIs — called subgraphs — into a single schema while sharing types and declaratively depending from data from other subgraphs. In fact, it is too good to keep it to GraphQL subgraphs only — what if you could bring other kinds of APIs and data sources into your federated graph without having to write a dedicated GraphQL server in front of them?\n\nIn the course of the hands-on workshop, we will:\nThe result will be a functioning GraphQL federated graph without GraphQL subgraphs, demonstrating that federation as a mechanism is useful beyond just GraphQL subgraphs.", "goers": "0", "seats": "0", "invite_only": "N", @@ -2598,7 +2598,7 @@ "event_end": "2025-09-09 15:45", "event_type": "Workshops", "description": "Unleash the Power of Federation with Hive Gateway Discover the possibilities of your GraphQL APIs! Learn how to use GraphQL Federation to unite various services and get to know Hive Gateway, an open-source GraphQL router. During this practical exercise, you will configure a simple federated gateway and execute queries across merged schemas. Discover important features such as built-in monitoring with OpenTelemetry, automatic query batching for optimal efficiency, strong security choices like JWT authentication and rate limiting, and GraphQL Subscriptions for real-time data. Find out for yourself how Hive Gateway makes it easier to create scalable and maintainable GraphQL.", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -2651,7 +2651,7 @@ "event_end": "2025-09-09 15:45", "event_type": "GraphQL Working Group", "description": "Curious about how observability is evolving in the GraphQL ecosystem? This session explores the current state of OpenTelemetry and its integration with GraphQL. We'll cover the fundamentals of OpenTelemetry, introduce the OpenTelemetry working group (https://github.com/graphql/otel-wg), and dive into tracing, logging, and metrics - all essential pillars of observability. You'll also learn how OpenTelemetry is being applied in distributed GraphQL architectures to improve performance monitoring and troubleshooting across services. Whether you're new to observability or looking to level up your GraphQL stack, this talk will bring you up to speed on where the community is heading.", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -2732,7 +2732,7 @@ "event_start": "2025-09-09 16:00", "event_end": "2025-09-09 16:40", "event_type": "GraphQL Working Group", - "description": "This discussion embarks on a thought experiment to redesign GraphQL from the ground up. We will explore the choices that might be made if we could start over, free from the constraints of existing implementations.\n\nThe session focuses on key areas where the current GraphQL specification has faced challenges and sparked debate within the community. Discussions will cover: Union Types: Exploring alternative approaches to improve flexibility and usability. Schema-Defined Nullability: Rethinking how nullability is handled to enhance clarity and consistency. Error Handling: Proposing new strategies for more robust and intuitive error management.\n\nThrough collaborative discussions and interactive exercises, participants will contribute insights and ideas, shaping a theoretical vision of what GraphQL 2.0 could look like. This thought exercise is designed to challenge assumptions and inspire innovative solutions.\n\nThe session will conclude with a focus on the practicalities of evolving GraphQL towards a 2.0 version in the real world, exploring how to address these design challenges while considering migration paths and maintaining backward compatibility.", + "description": "This discussion embarks on a thought experiment to redesign GraphQL from the ground up. We will explore the choices that might be made if we could start over, free from the constraints of existing implementations.\n\nThe session focuses on key areas where the current GraphQL specification has faced challenges and sparked debate within the community. Discussions will cover:\nThrough collaborative discussions and interactive exercises, participants will contribute insights and ideas, shaping a theoretical vision of what GraphQL 2.0 could look like. This thought exercise is designed to challenge assumptions and inspire innovative solutions.\n\nThe session will conclude with a focus on the practicalities of evolving GraphQL towards a 2.0 version in the real world, exploring how to address these design challenges while considering migration paths and maintaining backward compatibility.", "goers": "1", "seats": "0", "invite_only": "N", @@ -2836,7 +2836,7 @@ "event_start": "2025-09-09 16:00", "event_end": "2025-09-09 17:30", "event_type": "Workshops", - "description": "GraphQL is evolving — and with the new Composite Schema Specification, building distributed GraphQL systems has never been more standardized, flexible, and interoperable.\n\nIn this workshop, we'll dive into the core principles behind the Composite Schema Specification and demonstrate how they enable modular, scalable GraphQL architectures across teams and services. Whether you're coming from Node.js, Java, .NET, or any other stack — this specification is designed to work with you, not against you.\n\nAt the heart of this new standard is a powerful idea: composing GraphQL APIs in a strongly typed way. No more hidden fields. No more untyped extensions or brittle conventions.\n\nYou'll learn how to:\nDesign and build a distributed GraphQL system using the Composite Schema Specification Compose services with clear ownership and type-safe contracts Evolve your composite schema over time without breaking a sweat Solve real-world challenges like shared types, or auth propagation Operate composed schemas with observability, continuous integration, and confidence\nWhether you're building from scratch or evolving an existing monolith, this workshop will equip you with the tools and mental models to harness composition — the right way.", + "description": "GraphQL is evolving — and with the new Composite Schema Specification, building distributed GraphQL systems has never been more standardized, flexible, and interoperable.\n\nIn this workshop, we'll dive into the core principles behind the Composite Schema Specification and demonstrate how they enable modular, scalable GraphQL architectures across teams and services. Whether you're coming from Node.js, Java, .NET, or any other stack — this specification is designed to work with you, not against you.\n\nAt the heart of this new standard is a powerful idea: composing GraphQL APIs in a strongly typed way. No more hidden fields. No more untyped extensions or brittle conventions.\n\nYou'll learn how to:\n\nWhether you're building from scratch or evolving an existing monolith, this workshop will equip you with the tools and mental models to harness composition — the right way.", "goers": "0", "seats": "0", "invite_only": "N", @@ -2919,7 +2919,7 @@ "event_start": "2025-09-10 08:00", "event_end": "2025-09-10 17:30", "event_type": "Breaks / Networking / Special Events", - "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", + "description": "SECURITY NOTICE\nPlease keep all personal belongings in your possession during the conference. GraphQL, The Linux Foundation nor the Pakhuis de Zwijger are responsible for lost or stolen items. If something is misplaced, check in with an Event Staff member at the registration desk.", "goers": "0", "seats": "0", "invite_only": "N", @@ -2992,7 +2992,7 @@ "event_end": "2025-09-10 09:10", "event_type": "Developer Experience", "description": "Over the past year I have been evolving graphql-request into Graffle, a modular type safe GraphQL Client. Most of its features are realized as plugins so much so that it actually has fewer capabilities than graphql-request at its core! Graffle initially grew out of my desire to have a JS GraphQL client with a fully featured and type safe document builder which I couldn’t get from tools like GenQL or Zeus at the time.\n\nGraffle is still a mostly unknown work in progress but I am ready to begin talking about it and already some early adopters have taken to giving regular feedback. I think GraphQL can benefit from strengthened integrations with TypeScript. My past work on Nexus underscores that belief and I've been happy to see more recent tools enter the space like Pothos.", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -3265,7 +3265,7 @@ "event_end": "2025-09-10 10:10", "event_type": "Developer Experience", "description": "Managing separate API definitions for REST and GraphQL APIs that serve the same underlying data can be inefficient and lead to duplicated efforts. At Pinterest, we are streamlining our API definitions and unifying our data models with TypeSpec. TypeSpec allows us to define our API shapes once and generate API schemas in multiple forms such as OpenAPI, Protobuf, and now GraphQL!\n \nWe’ve developed an open-source TypeSpec GraphQL Emitter which generates valid GraphQL schemas directly from TypeSpec definitions.\n \nJoin us for an overview of how TypeSpec and the GraphQL Emitter can streamline your API workflow. We'll explore:\n \n* How TypeSpec's unified definition approach accelerates development across multiple API specs\n* The inner workings of our open-source GraphQL Emitter\n* Our wins and lessons learned while building the GraphQL Emitter\n \nThis talk will be perfect for anyone interested in GraphQL schema generation, unified API definitions, and vague Lord of the Rings references! See you there!", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -3311,7 +3311,7 @@ "event_end": "2025-09-10 10:10", "event_type": "GraphQL in Production", "description": "Have you ever wondered how GraphQL clients like Relay keep local data consistent across surfaces, ensuring that changes made within a session are seamlessly reflected across an application? In this talk, I'll delve into the concept of Local Data Consistency and explore how GraphQL clients at Meta, such as Relay, efficiently track and update changing GraphQL data locally, without introducing additional networking dependencies, and the UX benefits and features this unlocks.\n\nSpecifically, I’ll cover:\n- What even is Local Data Consistency, and why is it valuable to product developers?\n- How do you implement a data consistency engine from scratch?\n- How are advanced client-side features like offline mutation updates, asynchronous GraphQL request fetching, and more all made possible using a Local Data Consistency?", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -3357,7 +3357,7 @@ "event_end": "2025-09-10 10:50", "event_type": "Developer Experience", "description": "In theory, data loaders solve most \"N+1\" problems in GraphQL. In practice, they can be hard to implement, so they’re typically used only in performance-critical situations and often reactively, once inefficiencies surface. To achieve better performance, batching needs to be applied wherever possible.\n \nThis talk introduces batch resolvers, a more developer-friendly alternative to data loaders. While traditional GraphQL resolvers take a single input and produce a single output, batching resolvers take a list of inputs and return a list of outputs. A batch resolver can simply call a batch service API without worrying about data loaders.\n \nWhen a developer provides a batch resolver, our GraphQL server automatically aggregates individual data fetches into a single call to that resolver. It can also apply heuristics to improve aggregation, for example by consolidating different selection sets for the same entity into a single input. This design not only makes application developers’ lives easier, but also allows the server to better optimize query execution by coordinating batch dispatching as part of a broader execution strategy.", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -3502,7 +3502,7 @@ "event_end": "2025-09-10 10:50", "event_type": "GraphQL in Production", "description": "GraphQL provides flexibility in fetching data but this can prove challenging for caching. In this talk I cover the basics of caching in GraphQL such as layers you can cache at a high level. Layers such as the CDN, client side, server side, and database are touched upon with solutions from the community. The talk will also cover when to use each layer and what statistics to look at for improvement. I talk about how caching at multiple layers provides the best experience for the end user. By the end of this talk beginners will have a path forward to how they can cache at different layers for better performance.", - "goers": "2", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -3676,7 +3676,7 @@ "event_end": "2025-09-10 11:45", "event_type": "GraphQL in Production", "description": "Last year we introduced strict error handling - with @throwOnFieldError as an example of how this can be accomplished. This year, we’ll discuss how to safely roll it out.\n \nYou’ve had a GraphQL codebase that weakly handled server-side errors for years. Now, you have the tools (directives, hooks, handlers, and language features) that let you treat field errors properly. However, it’s a daunting task to suddenly explode queries en-masse by flipping a switch. This move is powerful, but requires a thoughtful and data-driven approach to do safely.\n \nIn this talk we’ll cover:\n* Preparing the groundwork for migration to stricter error handling\n* Using data to make informed decisions about fragment/query behavior\n* Gating your change at a singular point\n* Scaling the rollout to a large codebase\n* How we’re approaching this rollout at Meta", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -4094,7 +4094,7 @@ "event_end": "2025-09-10 14:50", "event_type": "GraphQL in Production", "description": "Imagine you have a decade old REST API codebase with thousands of daily commits by hundreds of engineers, how would you incrementally adopt GraphQL? How would the data models be compatible with both REST and GraphQL to avoid divergence? How…?\n\nWe will share Instagram’s journey from 100% REST API development to 95%+ new APIs developed in GraphQL over a two year period.", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -4168,7 +4168,7 @@ "event_end": "2025-09-10 14:30", "event_type": "GraphQL in Production", "description": "Any company that wants to innovate deals with change. Within GraphQL that often means introducing new fields but also deprecating old fields & types.\nThe faster you can get rid of these old fields & types the less complex your architecture is and less complexity means an easier time building new features!\n\nWe saw this problem and got tired of the endless “please migrate away from field X to field Y before Z” emails, which were often not even sent to the right group of consumers!\n\nWe automated this process by building a slack bot that uses production analytical data to figure out what clients are using deprecated fields and automated the communication!", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -4260,7 +4260,7 @@ "event_end": "2025-09-10 15:30", "event_type": "Developer Experience", "description": "Error handling is a critical aspect of developing robust GraphQL applications. Misuse or misunderstanding of errors can lead to applications that fail to function correctly, causing frustration for both developers and users.\n\nIn this talk, we explore how to effectively manage errors in GraphQL, ensuring they are wrapped in a useful way, the implementation details are hidden behind them, and common design pitfalls are avoided.", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "Studio - 5th Floor", @@ -4434,7 +4434,7 @@ "event_end": "2025-09-10 16:20", "event_type": "GraphQL in Production", "description": "Plugging an LLM into GraphQL sounds simple—until it drowns in thousands of fields, types, and connections. Most models today can’t reason effectively over large APIs without brittle prompt hacks or hardcoded shortcuts.\n\nModel Context Protocol (MCP) is the cutting-edge solution for enabling seamless, dynamic interactions between LLMs and external tooling. It standardizes the way models interact with various tools, breaking down barriers between APIs and AI systems.\n\nIn this talk, you’ll discover how to turn any GraphQL endpoint into an MCP-compatible server with minimal overhead. Reuse your existing GraphQL infrastructure to avoid reinventing authorization, schema management, and validation enabling scalable, robust LLM integrations. We’ll compare existing tools and automated schema discovery against hand-crafted mappers based on benchmarks of public GraphQL APIs. Join us to learn about our experiences and recommendations for your next GenAI project, powered by GraphQL.", - "goers": "0", + "goers": "1", "seats": "0", "invite_only": "N", "venue": "IJzaal - 5th Floor", @@ -4486,7 +4486,7 @@ "event_start": "2025-09-10 15:50", "event_end": "2025-09-10 16:20", "event_type": "GraphQL in Production", - "description": "Pinterest is adopting GraphQL. Given our app's size, we can't simply rewrite everything in one fell swoop. So, we created the Relay Migration API (RMA) — a set of tools to incrementally migrate your React components to consume GraphQL-shaped data while making requests to REST endpoints.\n\nI'll share how we've significantly evolved the RMA after migrating four key surfaces, focusing on the advanced challenges we faced:\n\nRMA recreates objects on every render by default, breaking components expecting stable references. We implemented a caching layer, similar to Relay's, to return consistent objects between renders. RMA originally read from static source objects, creating stale data when Redux state changed. Our solution: a selective subscription system that re-computes GraphQL data only when source fields change, keeping data current while eliminating unnecessary renders. And in cases where Redux and GraphQL schemas fundamentally differ, we built bidirectional mapping with schema validation to ensure data consistency.\nJoin us to learn how the Relay migration API has evolved and how it helps you accelerate your GraphQL migrations without disrupting existing applications!", + "description": "Pinterest is adopting GraphQL. Given our app's size, we can't simply rewrite everything in one fell swoop. So, we created the Relay Migration API (RMA) — a set of tools to incrementally migrate your React components to consume GraphQL-shaped data while making requests to REST endpoints.\n\nI'll share how we've significantly evolved the RMA after migrating four key surfaces, focusing on the advanced challenges we faced:\n\n\nJoin us to learn how the Relay migration API has evolved and how it helps you accelerate your GraphQL migrations without disrupting existing applications!", "goers": "2", "seats": "0", "invite_only": "N", @@ -4533,7 +4533,7 @@ "event_end": "2025-09-10 16:45", "event_type": "Keynote Sessions", "description": "This talk will give attendees an overview of the structure of GraphQL's official organizations: The GraphQL Foundation and the GraphQL Specification Project. It will get specific about the governance and roadmaps of each organization and their specific priorities in 2025 and beyond.\n\nIn my time serving in these various institutions, I've noticed that even the most active GraphQL practitioners aren't fully aware of what they are and what they do. Attendees will learn about the GraphQL Working Group, the Technical Steering Committee, and the Foundation's Governing Board. We'll also touch upon the various technical working groups and the new Community Working Group. The talk culminates in a call to action for folks to get involved.", - "goers": "2", + "goers": "3", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", @@ -4578,7 +4578,7 @@ "event_start": "2025-09-10 16:45", "event_end": "2025-09-10 17:00", "event_type": "Keynote Sessions", - "goers": "1", + "goers": "2", "seats": "0", "invite_only": "N", "venue": "Grote Zaal - 2nd Floor", diff --git a/scripts/sync-sched/speakers.json b/scripts/sync-sched/speakers.json index 605ae24175..7109f9d050 100644 --- a/scripts/sync-sched/speakers.json +++ b/scripts/sync-sched/speakers.json @@ -82,7 +82,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312971 + "~syncedDetailsAt": 1756282451111 }, { "username": "adam.sayah", @@ -116,12 +116,17 @@ "about": "Adam is currently a software development engineer at Amazon's Buyer Abuse Prevention team. For the last 4 years, Adam has been helping prevent returns abuse on Amazon.com. One of his projects was to consolidate the data the team needs into a single, cohesive, GraphQL API.", "location": "", "url": "", - "avatar": "//avatars.sched.co/b/ad/3141026/avatar.jpg.320x320px.jpg?90d", - "socialurls": [], + "avatar": "//avatars.sched.co/b/ad/3141026/avatar.jpg.320x320px.jpg?5d5", + "socialurls": [ + { + "service": "LinkedIn", + "url": "https://www.linkedin.com/in/adam-cervantes-236334201" + } + ], "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302380 + "~syncedDetailsAt": 1756283267177 }, { "username": "aditi_rajawat", @@ -151,7 +156,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "ajhingran", @@ -207,7 +212,7 @@ 2023, 2025 ], - "~syncedDetailsAt": 1756202312971 + "~syncedDetailsAt": 1756282451111 }, { "username": "alex_reilly.7ldur4l", @@ -270,7 +275,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "amy1908", @@ -345,7 +350,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756202312971 + "~syncedDetailsAt": 1756282451111 }, { "username": "andrei.bocan", @@ -361,7 +366,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756202312971 + "~syncedDetailsAt": 1756282451112 }, { "username": "andrew.doyle1", @@ -498,7 +503,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "ardatanrikulu", @@ -523,7 +528,7 @@ 2023, 2025 ], - "~syncedDetailsAt": 1756202312971 + "~syncedDetailsAt": 1756282451112 }, { "username": "arkenflame", @@ -585,7 +590,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "benjamin154", @@ -600,7 +605,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "benjie3", @@ -626,7 +631,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "BoD", @@ -641,7 +646,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "borisbesemer", @@ -656,7 +661,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "brandon.r.minnick", @@ -775,7 +780,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "christian.ernst", @@ -815,7 +820,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "christian.stangier", @@ -845,7 +850,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "danadajian", @@ -880,7 +885,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "danielle.man", @@ -925,7 +930,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756282673957 }, { "username": "dkuc", @@ -949,7 +954,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "donnasiqizhou", @@ -970,7 +975,7 @@ 2023, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "dotan1", @@ -994,7 +999,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "dotansimha", @@ -1078,7 +1083,7 @@ 2023, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "erikwrede2", @@ -1103,7 +1108,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "ernie.turner1", @@ -1142,7 +1147,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755876669495 + "~syncedDetailsAt": 1756283267177 }, { "username": "fbjork", @@ -1166,7 +1171,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236950902 + "~syncedDetailsAt": 1756282478611 }, { "username": "fionabronwen", @@ -1186,7 +1191,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236971470 + "~syncedDetailsAt": 1756282520506 }, { "username": "gabe210", @@ -1201,7 +1206,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302380 + "~syncedDetailsAt": 1756283267177 }, { "username": "gabrielschulhof", @@ -1323,7 +1328,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302380 + "~syncedDetailsAt": 1756283267177 }, { "username": "hello2358", @@ -1388,7 +1393,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236971470 + "~syncedDetailsAt": 1756282520506 }, { "username": "ivan.goncharov.ua", @@ -1408,7 +1413,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302380 + "~syncedDetailsAt": 1756283267177 }, { "username": "jamie855", @@ -1462,7 +1467,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302380 + "~syncedDetailsAt": 1756283267177 }, { "username": "jared_cheney.7rad60v", @@ -1502,7 +1507,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236971470 + "~syncedDetailsAt": 1756282520506 }, { "username": "jeff.auriemma", @@ -1528,7 +1533,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236971470 + "~syncedDetailsAt": 1756282520506 }, { "username": "jeff737", @@ -1548,7 +1553,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "jem28", @@ -1568,7 +1573,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "jens63", @@ -1612,7 +1617,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "jim.barton", @@ -1651,7 +1656,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302381 + "~syncedDetailsAt": 1756283267177 }, { "username": "jordaneldredge", @@ -1690,7 +1695,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302381 + "~syncedDetailsAt": 1756283267177 }, { "username": "juancarlosjr97", @@ -1714,7 +1719,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "kamilkisiela", @@ -1744,7 +1749,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "kbahl", @@ -1759,7 +1764,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312970 + "~syncedDetailsAt": 1756282451111 }, { "username": "keerthan.ekbote", @@ -1922,7 +1927,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "ldebruijn", @@ -1958,7 +1963,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "lee_byron.25krdom6", @@ -2012,7 +2017,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302381 + "~syncedDetailsAt": 1756283267177 }, { "username": "lyonwj1", @@ -2061,7 +2066,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "mail1232", @@ -2076,7 +2081,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202302381 + "~syncedDetailsAt": 1756283267177 }, { "username": "mansi.mittal", @@ -2091,7 +2096,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "marco.reni", @@ -2111,7 +2116,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "marion84", @@ -2155,7 +2160,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756236971471 + "~syncedDetailsAt": 1756282520506 }, { "username": "martijn.walraven", @@ -2171,7 +2176,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583283 }, { "username": "martinbonnin42", @@ -2186,7 +2191,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "marybriskin", @@ -2241,7 +2246,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "matteo.collina1", @@ -2261,7 +2266,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "mauricio.montalvo.guzman", @@ -2277,7 +2282,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583283 }, { "username": "meenakshi.dhanani1", @@ -2352,7 +2357,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583283 }, { "username": "michael.astle", @@ -2367,7 +2372,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "michael.bleigh", @@ -2406,7 +2411,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "omribruchim", @@ -2437,7 +2442,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583283 }, { "username": "patrick.arminio", @@ -2545,7 +2550,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "rachit_sengupta", @@ -2619,7 +2624,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "rickbijkerk54", @@ -2634,24 +2639,33 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "robert.balicki", "company": "Pinterest", "position": "Staff Engineer", "name": "Robert Balicki", - "about": "Robert is an engineer at Pinterest, where he helps the company adopt Relay and GraphQL. He was previously on the Relay team at Meta.", + "about": "Robert is an engineer at Pinterest, where he helps the company adopt Relay and GraphQL. He was previously on the Relay team at Meta. Check out Isograph! https://isograph.dev", "location": "", "url": "", - "avatar": "//avatars.sched.co/5/8b/18743858/avatar.jpg.320x320px.jpg?7d6", - "socialurls": [], + "avatar": "//avatars.sched.co/5/8b/18743858/avatar.jpg.320x320px.jpg?b95", + "socialurls": [ + { + "service": "Twitter", + "url": "https://x.com/StatisticsFTW" + }, + { + "service": "LinkedIn", + "url": "https://www.linkedin.com/in/robertbalicki/" + } + ], "_years": [ 2023, 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "robrichard87", @@ -2667,7 +2681,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "ruben.cagnie", @@ -2698,7 +2712,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "saihaj", @@ -2713,7 +2727,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367508 }, { "username": "saihajpreet.singh", @@ -2752,7 +2766,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367509 }, { "username": "sanvertarmur", @@ -2767,7 +2781,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367509 }, { "username": "sasanders26", @@ -2788,7 +2802,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "sasha177", @@ -2951,7 +2965,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "spencer211", @@ -2988,7 +3002,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "stefan239", @@ -3033,7 +3047,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202309111 + "~syncedDetailsAt": 1756284367509 }, { "username": "suresh_muthu", @@ -3140,7 +3154,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312970 + "~syncedDetailsAt": 1756282451111 }, { "username": "tim.hall.engr", @@ -3179,7 +3193,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312970 + "~syncedDetailsAt": 1756282451111 }, { "username": "tristan119", @@ -3274,7 +3288,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "vincent.desmares", @@ -3304,7 +3318,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312970 + "~syncedDetailsAt": 1756282451111 }, { "username": "vmjohnson999", @@ -3324,7 +3338,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756237016883 + "~syncedDetailsAt": 1756282583284 }, { "username": "watson17", @@ -3340,7 +3354,7 @@ 2024, 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282610669 }, { "username": "x65han", @@ -3355,7 +3369,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1756202312970 + "~syncedDetailsAt": 1756282451111 }, { "username": "yaacovcr", @@ -3439,7 +3453,7 @@ "_years": [ 2025 ], - "~syncedDetailsAt": 1755532095189 + "~syncedDetailsAt": 1756282673957 } ] } \ No newline at end of file diff --git a/scripts/sync-sched/sync.ts b/scripts/sync-sched/sync.ts index 6b92cf15c2..b902874ae6 100644 --- a/scripts/sync-sched/sync.ts +++ b/scripts/sync-sched/sync.ts @@ -96,6 +96,7 @@ async function sync( console.log("Getting schedule and speakers list...") const schedule = getSchedule(ctx) + console.dir(await schedule, { depth: 9, maxArrayLength: null }) const thisYearSpeakers = getSpeakers(ctx) const existingSchedule = readFile(scheduleFilePath, "utf-8").then(JSON.parse) const existingSpeakers = readFile(speakersFilePath, "utf-8").then(JSON.parse) diff --git a/src/app/conf/2025/schedule/[id]/format-description.tsx b/src/app/conf/2025/schedule/[id]/format-description.tsx index a7bea3d5c2..a34c253d98 100644 --- a/src/app/conf/2025/schedule/[id]/format-description.tsx +++ b/src/app/conf/2025/schedule/[id]/format-description.tsx @@ -1,35 +1,45 @@ -import React from "react" - const URL_REGEX = /https?:\/\/[^\s]+/g +const LINK_REGEX = /]*href\s*=\s*[^>]*)>/gi + +export function formatDescription(text: string): string { + // we coerce all existing anchor tags to have target="_blank" rel="noopener noreferrer" and typography-link class + const result = text.replace(LINK_REGEX, (_, attributes) => { + let attrs = attributes + + if (!attrs.includes("target=")) { + attrs += ' target="_blank"' + } + + if (!attrs.includes("rel=")) { + attrs += ' rel="noopener noreferrer"' + } + + if (!attrs.includes("class=")) { + attrs += ' class="typography-link"' + } else if (!attrs.includes("typography-link")) { + attrs = attrs.replace( + /class\s*=\s*["']([^"']*)/gi, + 'class="$1 typography-link', + ) + } + + return `` + }) -export function formatDescription(text: string): React.ReactNode { - const res: React.ReactNode[] = [] + // then we format plain URLs that are not already inside an anchor tag + return result.replace(URL_REGEX, (url, offset) => { + const beforeUrl = result.slice(0, offset) + const afterUrl = result.slice(offset + url.length) - let lastIndex = 0 - let match: RegExpExecArray | null + const lastOpenTag = beforeUrl.lastIndexOf("<") + const lastCloseTag = beforeUrl.lastIndexOf(">") + const nextCloseTag = afterUrl.indexOf(">") - while ((match = URL_REGEX.exec(text)) !== null) { - if (match.index > lastIndex) { - res.push(text.slice(lastIndex, match.index)) + if (lastOpenTag > lastCloseTag && nextCloseTag !== -1) { + return url } - res.push( - - {match[0].replace(/^https?:\/\//, "")} - , - ) - - lastIndex = match.index + match[0].length - } - - if (lastIndex < text.length) { - res.push(text.slice(lastIndex)) - } - - return <>{res} + const displayUrl = url.replace(/^https?:\/\//, "") + return `${displayUrl}` + }) } diff --git a/src/app/conf/2025/schedule/[id]/page.tsx b/src/app/conf/2025/schedule/[id]/page.tsx index c64649d3c6..becfe90122 100644 --- a/src/app/conf/2025/schedule/[id]/page.tsx +++ b/src/app/conf/2025/schedule/[id]/page.tsx @@ -104,13 +104,17 @@ export default function SessionPage({ params }: SessionProps) { )} -

- Session speakers -

- + {!!session.speakers?.length && ( + <> +

+ Session speakers +

+ + + )} {!!session.files?.length && ( <> @@ -255,9 +259,13 @@ function SessionDescription({ session }: { session: ScheduleSession }) { return (

Session description

-

- {formattedDescription} -

+

) } diff --git a/src/app/conf/2025/schedule/_components/schedule-session-card.tsx b/src/app/conf/2025/schedule/_components/schedule-session-card.tsx index e4ab5e66a4..3df8cc3654 100644 --- a/src/app/conf/2025/schedule/_components/schedule-session-card.tsx +++ b/src/app/conf/2025/schedule/_components/schedule-session-card.tsx @@ -8,6 +8,7 @@ import { MenuItems, Transition, } from "@headlessui/react" +import { stripHtml } from "string-strip-html" import { SchedSpeaker, ScheduleSession } from "@/app/conf/_api/sched-types" import { Anchor } from "@/app/conf/_design-system/anchor" @@ -132,10 +133,12 @@ export function ScheduleSessionCard({ )} - - - {session.venue} - + {session.venue && ( + + + {session.venue} + + )} {blockTimeFraction < 1 && ( @@ -176,7 +179,7 @@ function AddToCalendarLink({ title: eventTitle, start: session.event_start, end: session.event_end, - description: session.description, + description: stripHtml(session.description).result, location: session.venue, organizer: { name: `GraphQLConf ${new Date().getFullYear()}`, diff --git a/src/app/conf/2025/speakers/[id]/long-session-card.tsx b/src/app/conf/2025/speakers/[id]/long-session-card.tsx index 6b046b9da2..4353907a9b 100644 --- a/src/app/conf/2025/speakers/[id]/long-session-card.tsx +++ b/src/app/conf/2025/speakers/[id]/long-session-card.tsx @@ -15,6 +15,7 @@ import React, { Fragment } from "react" import { SessionTags } from "../../components/session-tags" import { Menu, MenuItem, MenuItems, Transition } from "@headlessui/react" import { MenuButton } from "@headlessui/react" +import { stripHtml } from "string-strip-html" export interface LongSessionCardProps extends React.HTMLAttributes { @@ -172,7 +173,7 @@ function AddToCalendarLink({ title: eventTitle, start: session.event_start, end: session.event_end, - description: session.description, + description: stripHtml(session.description).result, location: session.venue, organizer: { name: `GraphQLConf ${new Date().getFullYear()}`, diff --git a/src/app/conf/_api/sched-client.tsx b/src/app/conf/_api/sched-client.tsx index 68d7d1daa6..67e4ab7740 100644 --- a/src/app/conf/_api/sched-client.tsx +++ b/src/app/conf/_api/sched-client.tsx @@ -135,7 +135,9 @@ export async function getSchedule( ...session, event_type, event_subtype, - description: preprocessDescription(description), + description: preprocessDescription(description, { + allowSomeHtml: true, + }), } }) @@ -183,7 +185,10 @@ export async function getSpeakerDetails( return shapeSpeaker(data as SchedSpeaker) } -function preprocessDescription(description: string | undefined | null): string { +function preprocessDescription( + description: string | undefined | null, + options: { allowSomeHtml?: boolean } = {}, +): string { let res = description || "" // we respect manual line breaks @@ -191,7 +196,11 @@ function preprocessDescription(description: string | undefined | null): string { // respecting
  • and tags doesn't make sense, because speakers don't use them consistently // we'll improve how the descriptions look later down the tree in the session details page - return stripHtml(res).result + return stripHtml(res, { + ignoreTags: options.allowSomeHtml + ? ["a", "b", "i", "em", "strong", "code", "pre", "ul", "ol", "li"] + : [], + }).result } function shapeSpeaker(user: SchedSpeaker): SchedSpeaker { diff --git a/src/app/conf/_api/sched-types.ts b/src/app/conf/_api/sched-types.ts index 85cccd988c..1826342cdc 100644 --- a/src/app/conf/_api/sched-types.ts +++ b/src/app/conf/_api/sched-types.ts @@ -2,6 +2,9 @@ export type ScheduleSession = { id: string active: "Y" | "N" audience: string + /** + * can include HTML tags + */ description: string event_end: string event_start: string From fb7d28d6ea19d8daff053079005d39f470de4b3f Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Wed, 27 Aug 2025 10:48:19 +0200 Subject: [PATCH 2/2] Remove a console.log --- scripts/sync-sched/sync.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/sync-sched/sync.ts b/scripts/sync-sched/sync.ts index b902874ae6..6b92cf15c2 100644 --- a/scripts/sync-sched/sync.ts +++ b/scripts/sync-sched/sync.ts @@ -96,7 +96,6 @@ async function sync( console.log("Getting schedule and speakers list...") const schedule = getSchedule(ctx) - console.dir(await schedule, { depth: 9, maxArrayLength: null }) const thisYearSpeakers = getSpeakers(ctx) const existingSchedule = readFile(scheduleFilePath, "utf-8").then(JSON.parse) const existingSpeakers = readFile(speakersFilePath, "utf-8").then(JSON.parse)