Tag: apollo

  • GraphQL API Tutorial: Build a Typed API from Scratch with Apollo Server

    GraphQL is a query language for APIs that lets clients request exactly the data they need. Unlike REST with fixed endpoints, GraphQL exposes a single endpoint with a typed schema. It solves over-fetching, under-fetching, and the N+1 endpoint problem. Here’s how to build one from scratch.

    Core Concepts

    A GraphQL API is defined by its schema — a strongly typed contract. Queries read data, Mutations write data, Subscriptions stream real-time updates. Each field has a resolver — a function returning the data. The execution engine calls resolvers in parallel and assembles the response.

    Building with Apollo Server

    const { ApolloServer } = require('@apollo/server');
    const { startStandaloneServer } = require('@apollo/server/standalone');
    const { GraphQLError } = require('graphql');
    
    const typeDefs = `#graphql
        type User { id: ID!; name: String!; email: String!; posts: [Post!]!; postCount: Int! }
        type Post { id: ID!; title: String!; body: String!; published: Boolean!; author: User! }
        input CreateUserInput { name: String!; email: String! }
        input CreatePostInput { title: String!; body: String!; authorId: ID!; published: Boolean = false }
        type Query { users: [User!]!; user(id: ID!): User; posts(published: Boolean): [Post!]! }
        type Mutation { createUser(input: CreateUserInput!): User!; createPost(input: CreatePostInput!): Post!; publishPost(id: ID!): Post! }
    `;
    
    let users = [
        { id: '1', name: 'Alice Chen', email: 'alice@example.com' },
        { id: '2', name: 'Bob Martinez', email: 'bob@example.com' },
    ];
    let posts = [
        { id: '1', title: 'GraphQL Basics', body: 'An intro...', published: true, authorId: '1' },
        { id: '2', title: 'Advanced Queries', body: 'Deep dive...', published: true, authorId: '1' },
    ];
    let nextId = { user: 3, post: 3 };
    
    const resolvers = {
        Query: {
            users: () => users,
            user: (_, { id }) => users.find(u => u.id === id) || (() => { throw new GraphQLError('Not found'); })(),
            posts: (_, { published }) => published !== undefined ? posts.filter(p => p.published === published) : posts,
        },
        Mutation: {
            createUser: (_, { input }) => {
                if (users.some(u => u.email === input.email)) throw new GraphQLError('Email exists');
                const user = { id: String(nextId.user++), ...input };
                users.push(user);
                return user;
            },
            createPost: (_, { input }) => {
                const post = { id: String(nextId.post++), ...input };
                posts.push(post);
                return post;
            },
            publishPost: (_, { id }) => { const p = posts.find(p => p.id === id); p.published = true; return p; },
        },
        User: {
            posts: (user) => posts.filter(p => p.authorId === user.id),
            postCount: (user) => posts.filter(p => p.authorId === user.id).length,
        },
        Post: { author: (post) => users.find(u => u.id === post.authorId) },
    };
    
    (async () => {
        const server = new ApolloServer({ typeDefs, resolvers });
        const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
        console.log(`GraphQL API at ${url}`);
    })();

    Querying the API

    # Single request replaces 2+ REST calls
    query { users { name postCount posts { title published } } }
    
    # Precise data fetching — only the fields you need
    query { user(id: "1") { name email posts { title body } } }
    
    # Mutations
    mutation { createUser(input: { name: "Charlie", email: "charlie@example.com" }) { id name } }
    mutation { createPost(input: { title: "New Post", body: "Content...", authorId: "1", published: true }) { id title author { name } } }

    N+1 Problem & DataLoader

    50 posts each resolving author = 51 queries. DataLoader batches and caches: collects all requested IDs, makes one batched query, distributes results.

    GraphQL vs REST

    Choose GraphQL for multiple client types with different data needs, complex nested relationships, or rapidly evolving frontends. Stick with REST for simple CRUD, file uploads, caching-heavy workloads, or teams without GraphQL experience. They can coexist — REST for simple resources, GraphQL for complex aggregation.

    Further reading: GraphQL Docs | Apollo Server Docs