🎉 RamAPI v1.0 is now available! Read the Getting Started Guide
Documentation
Examples
GraphQL API

GraphQL API Example

Complete GraphQL server example with queries, mutations, and GraphQL Playground.

Note: This is a documentation example showing how to use RamAPI in your own project. The code assumes you have RamAPI installed via npm.

Prerequisites

# Install RamAPI
npm install ramapi
 
# Install dependencies
npm install graphql better-sqlite3
npm install -D @types/better-sqlite3

Overview

This example demonstrates:

  • GraphQL schema definition
  • Queries and mutations
  • Context and authentication
  • GraphQL Playground
  • Error handling
  • TypeScript integration

Complete Code

index.ts

import { createApp } from 'ramapi';
import { buildSchema } from 'graphql';
import Database from 'better-sqlite3';
 
// Database setup
const db = new Database('graphql.db');
 
db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    age INTEGER,
    created_at TEXT NOT NULL
  );
 
  CREATE TABLE IF NOT EXISTS posts (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    author_id TEXT NOT NULL,
    published INTEGER DEFAULT 0,
    created_at TEXT NOT NULL,
    FOREIGN KEY (author_id) REFERENCES users(id)
  );
`);
 
// GraphQL Schema
const schema = buildSchema(`
  type User {
    id: ID!
    name: String!
    email: String!
    age: Int
    posts: [Post!]!
    createdAt: String!
  }
 
  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    published: Boolean!
    createdAt: String!
  }
 
  type Query {
    users: [User!]!
    user(id: ID!): User
    posts(published: Boolean): [Post!]!
    post(id: ID!): Post
  }
 
  type Mutation {
    createUser(name: String!, email: String!, age: Int): User!
    updateUser(id: ID!, name: String, email: String, age: Int): User!
    deleteUser(id: ID!): Boolean!
 
    createPost(title: String!, content: String!, authorId: ID!, published: Boolean): Post!
    updatePost(id: ID!, title: String, content: String, published: Boolean): Post!
    deletePost(id: ID!): Boolean!
    publishPost(id: ID!): Post!
  }
`);
 
// Helper functions
function generateId(prefix: string): string {
  return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
 
// Resolvers
const root = {
  // Queries
  users: () => {
    return db.prepare('SELECT * FROM users').all();
  },
 
  user: ({ id }: { id: string }) => {
    const user = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    if (!user) throw new Error('User not found');
    return user;
  },
 
  posts: ({ published }: { published?: boolean }) => {
    if (published !== undefined) {
      return db.prepare('SELECT * FROM posts WHERE published = ?')
        .all(published ? 1 : 0);
    }
    return db.prepare('SELECT * FROM posts').all();
  },
 
  post: ({ id }: { id: string }) => {
    const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
    if (!post) throw new Error('Post not found');
    return post;
  },
 
  // Mutations
  createUser: ({ name, email, age }: { name: string; email: string; age?: number }) => {
    const id = generateId('user');
    const createdAt = new Date().toISOString();
 
    try {
      db.prepare(`
        INSERT INTO users (id, name, email, age, created_at)
        VALUES (?, ?, ?, ?, ?)
      `).run(id, name, email, age || null, createdAt);
 
      return db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    } catch (error: any) {
      if (error.message.includes('UNIQUE constraint failed')) {
        throw new Error('Email already exists');
      }
      throw error;
    }
  },
 
  updateUser: ({ id, name, email, age }: any) => {
    const user = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    if (!user) throw new Error('User not found');
 
    const updates: string[] = [];
    const values: any[] = [];
 
    if (name !== undefined) {
      updates.push('name = ?');
      values.push(name);
    }
    if (email !== undefined) {
      updates.push('email = ?');
      values.push(email);
    }
    if (age !== undefined) {
      updates.push('age = ?');
      values.push(age);
    }
 
    if (updates.length > 0) {
      values.push(id);
      db.prepare(`UPDATE users SET ${updates.join(', ')} WHERE id = ?`).run(...values);
    }
 
    return db.prepare('SELECT * FROM users WHERE id = ?').get(id);
  },
 
  deleteUser: ({ id }: { id: string }) => {
    const result = db.prepare('DELETE FROM users WHERE id = ?').run(id);
    return result.changes > 0;
  },
 
  createPost: ({ title, content, authorId, published }: any) => {
    const author = db.prepare('SELECT * FROM users WHERE id = ?').get(authorId);
    if (!author) throw new Error('Author not found');
 
    const id = generateId('post');
    const createdAt = new Date().toISOString();
 
    db.prepare(`
      INSERT INTO posts (id, title, content, author_id, published, created_at)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(id, title, content, authorId, published ? 1 : 0, createdAt);
 
    return db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
  },
 
  updatePost: ({ id, title, content, published }: any) => {
    const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
    if (!post) throw new Error('Post not found');
 
    const updates: string[] = [];
    const values: any[] = [];
 
    if (title !== undefined) {
      updates.push('title = ?');
      values.push(title);
    }
    if (content !== undefined) {
      updates.push('content = ?');
      values.push(content);
    }
    if (published !== undefined) {
      updates.push('published = ?');
      values.push(published ? 1 : 0);
    }
 
    if (updates.length > 0) {
      values.push(id);
      db.prepare(`UPDATE posts SET ${updates.join(', ')} WHERE id = ?`).run(...values);
    }
 
    return db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
  },
 
  deletePost: ({ id }: { id: string }) => {
    const result = db.prepare('DELETE FROM posts WHERE id = ?').run(id);
    return result.changes > 0;
  },
 
  publishPost: ({ id }: { id: string }) => {
    db.prepare('UPDATE posts SET published = 1 WHERE id = ?').run(id);
    return db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
  },
};
 
// Field resolvers for nested data
const fieldResolvers = {
  User: {
    posts: (user: any) => {
      return db.prepare('SELECT * FROM posts WHERE author_id = ?').all(user.id);
    },
  },
  Post: {
    author: (post: any) => {
      return db.prepare('SELECT * FROM users WHERE id = ?').get(post.author_id);
    },
    published: (post: any) => {
      return post.published === 1;
    },
  },
};
 
// Create app
const app = createApp({
  protocols: {
    graphql: {
      path: '/graphql',
      schema,
      rootValue: { ...root, ...fieldResolvers },
      playground: true,
      introspection: true,
    },
  },
});
 
// REST endpoints (optional - for comparison)
app.get('/api/users', async (ctx) => {
  const users = db.prepare('SELECT * FROM users').all();
  ctx.json({ users });
});
 
// Start server
await app.listen(3000);
console.log('🚀 GraphQL API running at http://localhost:3000/graphql');
console.log('📊 GraphQL Playground available at http://localhost:3000/graphql');

GraphQL Queries

Query All Users

query {
  users {
    id
    name
    email
    age
    posts {
      id
      title
      published
    }
  }
}

Response:

{
  "data": {
    "users": [
      {
        "id": "user-1",
        "name": "Alice Smith",
        "email": "alice@example.com",
        "age": 30,
        "posts": [
          {
            "id": "post-1",
            "title": "My First Post",
            "published": true
          }
        ]
      }
    ]
  }
}

Query Single User

query {
  user(id: "user-1") {
    id
    name
    email
    posts {
      title
      content
      createdAt
    }
  }
}

Query Posts

query {
  posts(published: true) {
    id
    title
    content
    author {
      name
      email
    }
    createdAt
  }
}

GraphQL Mutations

Create User

mutation {
  createUser(
    name: "Alice Smith"
    email: "alice@example.com"
    age: 30
  ) {
    id
    name
    email
    age
    createdAt
  }
}

Response:

{
  "data": {
    "createUser": {
      "id": "user-1705320000000-abc123",
      "name": "Alice Smith",
      "email": "alice@example.com",
      "age": 30,
      "createdAt": "2024-01-15T10:00:00.000Z"
    }
  }
}

Update User

mutation {
  updateUser(
    id: "user-1"
    name: "Alice Johnson"
  ) {
    id
    name
    email
  }
}

Create Post

mutation {
  createPost(
    title: "GraphQL with RamAPI"
    content: "Learn how to build GraphQL APIs with RamAPI"
    authorId: "user-1"
    published: true
  ) {
    id
    title
    content
    author {
      name
    }
    published
    createdAt
  }
}

Publish Post

mutation {
  publishPost(id: "post-1") {
    id
    title
    published
  }
}

Delete Post

mutation {
  deletePost(id: "post-1")
}

With Variables

query GetUser($userId: ID!) {
  user(id: $userId) {
    id
    name
    email
    posts {
      title
      published
    }
  }
}

Variables:

{
  "userId": "user-1"
}

Frontend Integration (Apollo Client)

// apollo-client.ts
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
 
export const client = new ApolloClient({
  uri: 'http://localhost:3000/graphql',
  cache: new InMemoryCache(),
});
 
// Queries
export const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
      posts {
        id
        title
      }
    }
  }
`;
 
export const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      age
      posts {
        id
        title
        content
        published
      }
    }
  }
`;
 
// Mutations
export const CREATE_USER = gql`
  mutation CreateUser($name: String!, $email: String!, $age: Int) {
    createUser(name: $name, email: $email, age: $age) {
      id
      name
      email
    }
  }
`;
 
export const CREATE_POST = gql`
  mutation CreatePost(
    $title: String!
    $content: String!
    $authorId: ID!
    $published: Boolean
  ) {
    createPost(
      title: $title
      content: $content
      authorId: $authorId
      published: $published
    ) {
      id
      title
      content
      published
    }
  }
`;
// UserList.tsx
import { useQuery, useMutation } from '@apollo/client';
import { GET_USERS, CREATE_USER } from './apollo-client';
 
export function UserList() {
  const { loading, error, data } = useQuery(GET_USERS);
  const [createUser] = useMutation(CREATE_USER, {
    refetchQueries: [{ query: GET_USERS }],
  });
 
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
 
  const handleCreate = async () => {
    await createUser({
      variables: {
        name: 'New User',
        email: 'new@example.com',
        age: 25,
      },
    });
  };
 
  return (
    <div>
      <button onClick={handleCreate}>Create User</button>
      <ul>
        {data.users.map((user: any) => (
          <li key={user.id}>
            {user.name} - {user.email}
            <ul>
              {user.posts.map((post: any) => (
                <li key={post.id}>{post.title}</li>
              ))}
            </ul>
          </li>
        ))}
      </ul>
    </div>
  );
}

cURL Examples

Query

curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ users { id name email } }"
  }'

Mutation

curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { createUser(name: \"Alice\", email: \"alice@example.com\") { id name } }"
  }'

With Variables

curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query GetUser($id: ID!) { user(id: $id) { name email } }",
    "variables": { "id": "user-1" }
  }'

Error Handling

GraphQL errors are returned in the errors array:

{
  "errors": [
    {
      "message": "User not found",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["user"]
    }
  ],
  "data": {
    "user": null
  }
}

See Also