मुख्य सामग्री पर जाएं

MongoDB और GraphQL: एक परफेक्ट मेल

जानें कि TypeScript और MongoDB के साथ GraphQL API कैसे बनाई जाए—एक ऐसा संयोजन जो न केवल कुशल है, बल्कि काम करने में भी बेहद आसान है।
अपडेट किया गया 25 सित॰ 2026  · 11 मि॰ पढ़ें

AI के साथ खोजें

ChatGPTClaudePerplexity

GraphQL APIs बनाने का एक शक्तिशाली और कुशल तरीका है। क्लाइंट API से उसी तरह क्वेरी करता है जैसे वह किसी डेटाबेस से करता है, और वह API केवल वही डेटा लौटाती है जिसकी उसने मांग की है, जिससे अक्सर रिस्पॉन्स पेलोड कम होता है और प्रतिक्रिया समय बेहतर होता है। आज की दुनिया में कम प्रतिक्रिया समय बेहद महत्वपूर्ण है।

जब GraphQL API को MongoDB के साथ जोड़ा जाता है, तो आपको न केवल तेज प्रतिक्रिया समय मिलता है, बल्कि शुरुआत से अंत तक एकसमान डेटा फॉर्मैट भी मिलता है।

कल्पना कीजिए: आपका क्लाइंट एक GraphQL क्वेरी चला रहा है और वह क्वेरी JSON जैसी दिखती है। जब डेटा आपकी एप्लिकेशन तक पहुंचता है—जो मान लीजिए इस उदाहरण में TypeScript है—तो आप अपनी एप्लिकेशन में JSON जैसे डेटा फॉर्मैट के साथ काम कर रहे होते हैं। एक कदम आगे, जब आप MongoDB के साथ काम करते हैं, तो MongoDB से आने-जाने वाला डेटा भी JSON जैसा होगा। तो आपको प्रदर्शन के साथ-साथ एक सुसंगत डेटा अनुभव मिलता है। अपने डेटा को बदलने और फॉर्मैट करने की चिंता करने के बजाय, आप अपनी एप्लिकेशन के यूज़र अनुभव पर ध्यान दे सकते हैं, न कि डेटाबेस और टूलिंग पर।

इस ट्यूटोरियल में, हम देखेंगे कि अपने GraphQL API में MongoDB का उपयोग करना, इस बार TypeScript के साथ, कितना आसान है।

अनिवार्य पूर्व-आवश्यकताएँ

इस विशेष ट्यूटोरियल के लिए, शुरू करने से पहले हमें कुछ चीज़ें तैयार रखनी होंगी:

  • एक MongoDB Atlas क्लस्टर
  • Node.js 22+
  • MongoDB Node.js Driver 6.x

हम इस ट्यूटोरियल में डेटाबेस के नज़रिए से कोई बहुत जटिल काम नहीं करेंगे, इसलिए MongoDB का कोई भी टियर क्लस्टर काम करेगा, यहाँ तक कि फ्री टियर भी। हालांकि, इस ट्यूटोरियल में हम MongoDB Atlas क्लस्टर की प्रोविजनिंग पर चर्चा नहीं करेंगे। अपेक्षा है कि आपके पास यूज़र नियम और नेटवर्क नियम पहले से कॉन्फ़िगर हों। यदि आपको शुरुआत करने में मदद चाहिए, तो इस विषय पर Get Started With Atlas दस्तावेज़ देखें।

आपको एक Node.js एप्लिकेशन बनाया होना चाहिए। यदि आप चाहें, तो आप निम्नलिखित कमांड चला सकते हैं:

mkdir graphql_example
cd graphql_example
npm init -y

उपरोक्त कमांड एक नया प्रोजेक्ट डायरेक्टरी और एक बेसिक package.json फ़ाइल बनाते हैं। हम इस प्रोजेक्ट में TypeScript, Express Framework, MongoDB, और Apollo का उपयोग करेंगे। इन्हें निम्नलिखित कमांड के साथ इंस्टॉल किया जा सकता है:

npm install @apollo/server@4 body-parser cors dotenv express@4 graphql mongodb
npm install @types/cors @types/express@4 @types/node tsx typescript --save-dev

ये कमांड डिपेंडेंसीज़ के साथ-साथ TypeScript में उपयोग के लिए टाइप डेफिनिशन भी इंस्टॉल कर देंगी।

हमारी पूर्व-आवश्यकताओं के अंतिम भाग के रूप में, आपको TypeScript के लिए एक कॉन्फ़िगरेशन फ़ाइल की आवश्यकता होगी। अपने प्रोजेक्ट के रूट पर, निम्न JSON के साथ एक tsconfig.json फ़ाइल बनाएं:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "moduleResolution": "node",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

यदि किसी भी समय आप पूरे प्रोजेक्ट को देखना चाहें या इसे स्वयं आज़माना चाहें, तो आप इसे GitHub पर देख सकते हैं।

Apollo, Express, और MongoDB डिपेंडेंसीज़ के साथ नींव तैयार करें

प्रोजेक्ट संरचना के नज़रिए से, हम निम्नलिखित का उपयोग करेंगे:

  • src/database/connection.ts
  • src/database/userService.ts
  • src/resolvers/index.ts
  • src/schema/typeDefs.ts
  • src/types/index.ts
  • src/index.ts
  • .env

src/index.ts फ़ाइल में हमारा सारा Express Framework और Apollo बूटस्ट्रैपिंग होगा। इसमें सर्वर शुरू करना, GraphQL एंडपॉइंट्स परिभाषित करना आदि शामिल है। src/types/index.ts फ़ाइल में हमारी एप्लिकेशन के भीतर हमारे डेटाबेस डेटा मॉडल के साथ काम करने के लिए TypeScript डेफिनिशन होंगे। इसे src/schema/typeDefs.ts फ़ाइल से न मिलाएँ, जो कि भले ही समान लगे, लेकिन GraphQL टाइप डेफिनिशन पर केंद्रित है।

अब हमारे पास database और resolvers डायरेक्टरी की फ़ाइलें बचती हैं। हमारी एप्लिकेशन और डेटाबेस लॉजिक का अधिकांश हिस्सा इन्हीं फ़ाइलों में होगा, जिसे हम जल्द ही देखेंगे।

फिलहाल, src/index.ts फ़ाइल में अपना सर्वर सेटअप करने पर ध्यान दें:

import 'dotenv/config';
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import express from 'express';
import http from 'http';
import cors from 'cors';
import bodyParser from 'body-parser';
import { typeDefs } from './schema/typeDefs';
import { resolvers } from './resolvers';

async function startServer() {
  try {

    const app = express();
    
    const httpServer = http.createServer(app);

    const server = new ApolloServer({
      typeDefs,
      resolvers,
    });

    await server.start();

    app.use(
      '/graphql',
      cors<cors.CorsRequest>(),
      bodyParser.json(),
      expressMiddleware(server)
    );

    const PORT = process.env.PORT || 3000;
    await new Promise<void>((resolve) => httpServer.listen({ port: PORT }, resolve));
    
    console.log(Server ready at http://localhost:${PORT}/graphql);

  } catch (error) {
    console.error('Error starting server:', error);
    process.exit(1);
  }
}

startServer();

जैसे-जैसे हम आगे बढ़ेंगे, हम ऊपर दी गई फ़ाइल का विस्तार करेंगे।

शुरू करने के लिए, हम अपनी सभी प्रोजेक्ट डिपेंडेंसीज़ इम्पोर्ट कर रहे हैं। अगला कदम है अपने Express Framework सर्वर और Apollo मिडलवेयर को कॉन्फ़िगर करना।

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

हमने अभी src/schema/typeDefs.ts और src/resolvers/index.ts फ़ाइलों में कुछ नहीं जोड़ा है, लेकिन इस चरण के हिस्से के रूप में हम सब कुछ तैयार कर रहे हैं। जब तक हम अन्य फ़ाइलों की कमी को पूरा नहीं करते, तब तक सर्वर को बिना त्रुटियों के चलाना सफल नहीं होगा।

यह उल्लेख करना उचित है कि यह विशेष उदाहरण सभी ओरिजिन से अनुरोधों की अनुमति देता है, इसकी वजह निम्न पंक्ति है:

cors<cors.CorsRequest>(),

यह विकास परिवेश के लिए तो ठीक है, लेकिन प्रोडक्शन डिप्लॉयमेंट में केवल आवश्यक ओरिजिन जोड़ने पर विचार करें।

TypeScript के साथ Node.js एप्लिकेशन से MongoDB के साथ इंटरैक्ट करना

हालाँकि हमारे पास Express Framework और Apollo सर्वर की नींव मौजूद है, हमने अपनी एप्लिकेशन में MongoDB डेटाबेस को कॉन्फ़िगर नहीं किया है। कोड में जाने से पहले, हमें अपने .env फ़ाइल में कनेक्शन जानकारी जोड़नी होगी:

MONGODB_URI="mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/?appName=devrel-graphql"
DB_NAME="graphql_example"

<USERNAME>, <PASSWORD>, और <HOST> को अपने MongoDB Atlas क्लस्टर की जानकारी से बदलें, जो आपको अपने MongoDB Atlas डैशबोर्ड में मिल जाएगी।

अब, अपनी एप्लिकेशन के लिए डेटाबेस कनेक्शन लॉजिक स्थापित करें। **src/database/connection.ts** फ़ाइल में, निम्न कोड जोड़ें:

import { MongoClient, Db } from 'mongodb';

let db: Db | null = null;
let client: MongoClient | null = null;

export async function connectToDatabase(): Promise<Db> {
  if (db) {
    return db;
  }

  try {
    const MONGODB_URI = process.env.MONGODB_URI;
    if (!MONGODB_URI) {
      throw new Error('MONGODB_URI is not set');
    }
    const DB_NAME = process.env.DB_NAME || 'graphql_example';
    const APP_NAME = process.env.APP_NAME || 'devrel-graphql';

    console.log('Connecting to MongoDB...');
    
    client = new MongoClient(MONGODB_URI, { appName: APP_NAME });
    await client.connect();
    
    db = client.db(DB_NAME);
    
    console.log(Successfully connected to MongoDB database: ${DB_NAME});
    
    return db;
  } catch (error) {
    console.error('MongoDB connection error:', error);
    throw error;
  }
}

export function getDatabase(): Db {
  if (!db) {
    throw new Error('Database not initialized. Call connectToDatabase() first.');
  }
  return db;
}

export async function closeDatabase(): Promise<void> {
  if (client) {
    await client.close();
    db = null;
    client = null;
    console.log('MongoDB connection closed');
  }
}

यहाँ विचार यह है कि हम एक सिंगलटन कनेक्शन बना रहे हैं। आपकी एप्लिकेशन में MongoDB को काम कराने के और भी कई तरीके हैं, लेकिन इस एप्लिकेशन के लिए हम यही तरीका चुन रहे हैं।

connectToDatabase फ़ंक्शन **.env** फ़ाइल से कनेक्शन जानकारी लेगा, कनेक्ट करेगा, और वह डेटाबेस लौटाएगा जिसे आपने परिभाषित किया है। यदि यह विफल होता है, तो त्रुटि पकड़ी जाएगी और आगे फेंकी जाएगी। आपको सर्वर सेटअप करते समय, जैसा कि पिछली फ़ाइल में देखा गया है, केवल connectToDatabase फ़ंक्शन का उपयोग करने की आवश्यकता होगी।

getDatabase फ़ंक्शन का उपयोग तब किया जाएगा जब भी हमारी एप्लिकेशन को कोई अनुरोध प्राप्त होगा। यह हमें हमारे कनेक्शन के वर्तमान इंस्टेंस को प्राप्त करने देगा।

अंत में, यदि हम चीज़ों को साफ़-सुथरा रखना चाहें, तो हमारे पास closeDatabase फ़ंक्शन है, जो हमारे सर्वर के बंद होने पर डेटाबेस से कनेक्शन बंद कर देगा। इसे शामिल करना आपके ऊपर है, लेकिन इसे रखना अच्छा अभ्यास है।

हमारे डेटा ट्रांसफ़र ऑब्जेक्ट्स (DTOs) के लिए TypeScript टाइप्स लेना, डेटाबेस लॉजिक पर जाने से पहले, एक अच्छा विचार होगा:

export interface User {
  id: string;
  name: string;
  email: string;
  age?: number;
  createdAt: string;
}

export interface CreateUserInput {
  name: string;
  email: string;
  age?: number;
}

export interface UpdateUserInput {
  id: string;
  name?: string;
  email?: string;
  age?: number;
}

ऊपर दिए गए इंटरफेस, जो **src/types/index.ts** फ़ाइल में हैं, हमारी API द्वारा मैनेज किए जाते हैं, लेकिन हमारे डेटाबेस लॉजिक को इनके बारे में अवगत होना चाहिए। आप इसे अभी देखेंगे।

यह हमें हमारे वास्तविक डेटाबेस इंटरेक्शन—create, retrieve, update, delete (CRUD)—तक लाता है, और यह src/database/userService.ts फ़ाइल में किया जाता है। विचार यह है कि हमारे GraphQL रिजॉल्वर एप्लिकेशन लॉजिक संभालेंगे और इस सर्विस फ़ाइल में फ़ंक्शंस को कॉल करेंगे, और सर्विस डेटाबेस लॉजिक संभालेगी।

src/database/userService.ts फ़ाइल में हमारे पास निम्न कोड है:

import { Collection, ObjectId } from 'mongodb';
import { getDatabase } from './connection';
import { User, CreateUserInput, UpdateUserInput } from '../types';

interface UserDocument {
  _id: ObjectId;
  name: string;
  email: string;
  age?: number;
  createdAt: Date;
}

export class UserService {
  private collection: Collection<UserDocument>;

  constructor() {
    const db = getDatabase();
    this.collection = db.collection<UserDocument>('users');
  }

  private toUser(doc: UserDocument): User {
    return {
      id: doc._id.toString(),
      name: doc.name,
      email: doc.email,
      age: doc.age,
      createdAt: doc.createdAt.toISOString(),
    };
  }

  async getAllUsers(): Promise<User[]> {
    const users = await this.collection.find().toArray();
    return users.map(doc => this.toUser(doc));
  }

  async getUserById(id: string): Promise<User | null> {
    try {
      const doc = await this.collection.findOne({ _id: new ObjectId(id) });
      return doc ? this.toUser(doc) : null;
    } catch (error) {
      return null;
    }
  }

  async createUser(input: CreateUserInput): Promise<User> {
    const newUser: Omit<UserDocument, '_id'> = {
      name: input.name,
      email: input.email,
      age: input.age,
      createdAt: new Date(),
    };

    const result = await this.collection.insertOne(newUser as UserDocument);
    
    const createdUser = await this.collection.findOne({ _id: result.insertedId });
    
    if (!createdUser) {
      throw new Error('Failed to create user');
    }

    return this.toUser(createdUser);
  }

  async updateUser(input: UpdateUserInput): Promise<User | null> {
    try {
      const updateFields: Partial<Omit<UserDocument, '_id'>> = {};
      
      if (input.name !== undefined) updateFields.name = input.name;
      if (input.email !== undefined) updateFields.email = input.email;
      if (input.age !== undefined) updateFields.age = input.age;

      const result = await this.collection.findOneAndUpdate(
        { _id: new ObjectId(input.id) },
        { $set: updateFields },
        { returnDocument: 'after' }
      );

      return result ? this.toUser(result) : null;
    } catch (error) {
      return null;
    }
  }

  async deleteUser(id: string): Promise<boolean> {
    try {
      const result = await this.collection.deleteOne({ _id: new ObjectId(id) });
      return result.deletedCount > 0;
    } catch (error) {
      return false;
    }
  }

  async createIndexes(): Promise<void> {
    await this.collection.createIndex({ email: 1 }, { unique: true });
    await this.collection.createIndex({ createdAt: -1 });
    console.log('Database indexes created');
  }
}

ऊपर सबसे पहले आप यह इंटरफेस नोट करेंगे:

interface UserDocument {
  _id: ObjectId;
  name: string;
  email: string;
  age?: number;
  createdAt: Date;
}

यह हमारे User इंटरफेस जैसा दिखता है, मगर बिल्कुल वैसा नहीं है। UserDocument हमारे डेटाबेस के काम करने के तरीके के अधिक सख्त अनुरूप है। हम अपने डेटाबेस में ObjectId के साथ काम कर रहे हैं, लेकिन अपनी एप्लिकेशन में स्ट्रिंग हैश के साथ। MongoDB में id को id के बजाय _id के रूप में दर्शाया जाता है, जैसा कि हम User इंटरफेस में देखते हैं।

अब हम UserService क्लास पर आते हैं।

constructor मेथड के माध्यम से डेटाबेस इंस्टेंस से अपनी कलेक्शन का हैंडल प्राप्त करने के बाद, हमारे पास निम्नलिखित फ़ंक्शंस की श्रृंखला है:

  • toUser
  • getAllUsers
  • getUserById
  • createUser
  • updateUser
  • deleteUser
  • createIndexes

toUser फ़ंक्शन मूलतः एक हेल्पर है जो User और UserDocument के बीच रूपांतरण करता है। हम डेटाबेस को UserDocument भेजते और उससे प्राप्त करते हैं और हमें इसे रूपांतरित करना होता है ताकि एप्लिकेशन से User भेज सकें और प्राप्त कर सकें।

async createIndexes(): Promise<void> {
	await this.collection.createIndex({ email: 1 }, { unique: true });
	await this.collection.createIndex({ createdAt: -1 });
	console.log('Database indexes created');
}

createIndexes फ़ंक्शन कुछ उदाहरण इंडेक्स बनाता है जो हमारे लिए उपयोगी हो सकते हैं। डिफ़ॉल्ट रूप से, एक इंडेक्स केवल _id फ़ील्ड पर होता है, जो कि कुछ मुट्ठी भर डॉ큐मेंट वाले डेमो के लिए ठीक है, लेकिन जैसे-जैसे आपकी एप्लिकेशन बढ़ती है, अपने डेटा और क्वेरी आवश्यकताओं के आधार पर उचित इंडेक्स निर्माण को नज़रअंदाज़ न करें। ऊपर दिए गए दो इंडेक्स केवल उदाहरण हैं।

अब हम प्रत्येक सर्विस फ़ंक्शन को देख सकते हैं, getAllUsers फ़ंक्शन से शुरू करते हुए:

async getAllUsers(): Promise<User[]> {
	const users = await this.collection.find().toArray();
	return users.map(doc => this.toUser(doc));
}

ऊपर दिया गया फ़ंक्शन कलेक्शन में सभी डॉ큐मेंट ढूंढेगा क्योंकि find फ़ंक्शन में कोई फ़िल्टर मानदंड नहीं है। परिणामों को हमारे toUser फ़ंक्शन से बदला जाता है और एप्लिकेशन को लौटा दिया जाता है, जो आगे चलकर हमारे Apollo रिजॉल्वर होंगे।

async getUserById(id: string): Promise<User | null> {
	try {
	  const doc = await this.collection.findOne({ _id: new ObjectId(id) });
	  return doc ? this.toUser(doc) : null;
	} catch (error) {
	  return null;
	}
}

getByUserId फ़ंक्शन समान है, लेकिन इस बार हम findOne का उपयोग कर रहे हैं और फ़िल्टर मानदंड निर्दिष्ट कर रहे हैं—यहाँ मानदंड यह है कि हम यूनिक _id फ़ील्ड के आधार पर एक ही यूज़र चाहते हैं। क्योंकि User इंटरफेस में id फ़ील्ड एक स्ट्रिंग है, हमें इसे MongoDB के भीतर अपनी क्वेरी के लिए उपयुक्त बनाने हेतु ObjectId में रैप करना होगा।

अब हम createUser फ़ंक्शन के माध्यम से MongoDB के भीतर डेटा बनाते हैं:

async createUser(input: CreateUserInput): Promise<User> {
	const newUser: Omit<UserDocument, '_id'> = {
	  name: input.name,
	  email: input.email,
	  age: input.age,
	  createdAt: new Date(),
	};
	
	const result = await this.collection.insertOne(newUser as UserDocument);
	
	const createdUser = await this.collection.findOne({ _id: result.insertedId });
	
	if (!createdUser) {
	  throw new Error('Failed to create user');
	}
	
	return this.toUser(createdUser);
}

हमने insertOne फ़ंक्शन से डेटा जोड़ने के बाद, लौटे हुए insertedId का उपयोग करके उसी डेटा को क्वेरी किया। हम ऐसा इसलिए करते हैं क्योंकि GraphQL API में, जब डेटा बनाया जाता है, तो आम तौर पर अपेक्षा रहती है कि उसी म्यूटेशन में आप उस डेटा को क्वेरी भी कर सकें।

updateUser फ़ंक्शन थोड़ा अलग है:

async updateUser(input: UpdateUserInput): Promise<User | null> {
	try {
	  const updateFields: Partial<Omit<UserDocument, '_id'>> = {};
	  
	  if (input.name !== undefined) updateFields.name = input.name;
	  if (input.email !== undefined) updateFields.email = input.email;
	  if (input.age !== undefined) updateFields.age = input.age;
	
	  const result = await this.collection.findOneAndUpdate(
		{ _id: new ObjectId(input.id) },
		{ $set: updateFields },
		{ returnDocument: 'after' }
	  );
	
	  return result ? this.toUser(result) : null;
	} catch (error) {
	  return null;
	}
}

इस एप्लिकेशन में, हम केवल अपने डेटा के name, email, और age फ़ील्ड्स पर अपडेट की अनुमति दे रहे हैं। ये सभी फ़ील्ड वैकल्पिक हैं, इसलिए यदि ये मौजूद हों तो हमें इन्हें अपने अपडेट मानदंड में सेट करना होगा। 

यह हमें findOneAndUpdate फ़ंक्शन तक लाता है।

इस फ़ंक्शन का पहला ऑब्जेक्ट पैरामीटर फ़िल्टर मानदंड है। हम केवल वहाँ अपडेट करने में रुचि रखते हैं जहाँ _id मेल खाता हो। किसी भी मेल के लिए, हम $set ऑपरेटर का उपयोग करते हैं ताकि अपने updateFields ऑब्जेक्ट में मौजूद फ़ील्ड्स को बदलें या बनाएँ। जो फ़ील्ड इस ऑब्जेक्ट में नहीं हैं, वे बदले नहीं जाएँगे। अंत में, क्योंकि अपेक्षा है कि हम ढूंढ भी रहे हैं और अपडेट भी कर रहे हैं, केवल अपडेट नहीं, तो हम returnDocument को after सेट करते हैं ताकि प्रतिक्रिया में बदला हुआ डॉ큐मेंट मिले, न कि मूल डॉ큐मेंट। यदि हमने updateOne किया होता न कि findOneAndUpdate, तो हमें केवल अपडेट ऑपरेशन के बारे में जानकारी मिलती, डेटा नहीं।

इस API के लिए अंतिम CRUD ऑपरेशन MongoDB से डेटा हटाने का है, और यह deleteUser फ़ंक्शन के माध्यम से किया जाता है:

async deleteUser(id: string): Promise<boolean> {
	try {
	  const result = await this.collection.deleteOne({ _id: new ObjectId(id) });
	  return result.deletedCount > 0;
	} catch (error) {
	  return false;
	}
}

ऊपर दिया गया deleteOne फ़ंक्शन हमारे findOne जैसा दिखता है क्योंकि हम _id के आधार पर फ़िल्टर मानदंड का उपयोग कर रहे हैं। अंतर यह है कि कोई भी मेल—इस मामले में अधिकतम एक डॉ큐मेंट—हटा दिया जाएगा।

इस बिंदु पर, डेटाबेस लॉजिक तैयार है। इसे अंतिम रूप देने के लिए, हमें **src/index.ts** फ़ाइल से कनेक्ट करना होगा:

// Previous imports here...
import { connectToDatabase, closeDatabase } from './database/connection';
import { UserService } from './database/userService';

async function startServer() {
  try {

    await connectToDatabase();
    
    const userService = new UserService();
    await userService.createIndexes();
    
    // The rest of the file here...
}

ऊपर दिए गए स्निपेट में, src/index.ts फ़ाइल का अधिकांश हिस्सा हटाया गया है। उद्देश्य केवल connectToDatabase फ़ंक्शन के उपयोग और इंडेक्स बनाने को दिखाना था। स्पष्ट रूप से कहें, तो अपने startServer फ़ंक्शन के आरंभ में ये तीन पंक्तियाँ जोड़ दें।

अब हम API लॉजिक पर ध्यान केंद्रित कर सकते हैं।

GraphQL टाइप्स, रिजॉल्वर, और कस्टम एप्लिकेशन लॉजिक परिभाषित करें

हमारे पास पहले से ही डेटाबेस के साथ काम करने और अपनी एप्लिकेशन में डेटा पास करने के लिए TypeScript टाइप्स परिभाषित हैं। हालाँकि, हमारे पास GraphQL टाइप डेफिनिशन नहीं हैं—वे चीज़ें जो यह तय करती हैं कि उपयोगकर्ता हमारी API का उपयोग करते समय किन चीज़ों के साथ इंटरैक्ट कर सकता है।

src/schema/typeDefs.ts फ़ाइल में, निम्न जोड़ें:

export const typeDefs = #graphql
  type User {
    id: ID!
    name: String!
    email: String!
    age: Int
    createdAt: String!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
  }

  type Mutation {
    createUser(name: String!, email: String!, age: Int): User!
    updateUser(id: ID!, name: String, email: String, age: Int): User
    deleteUser(id: ID!): Boolean!
  }
;

User टाइप वह है जिसे क्लाइंट किसी भी दिए गए क्वेरी में अनुरोध कर सकता है। इस उदाहरण में यह User इंटरफ़ेस से मेल खाता है, लेकिन इसकी अनिवार्यता नहीं है। उदाहरण के लिए, हो सकता है कि आपके User इंटरफ़ेस में ऐसा डेटा हो जिसे आप कभी क्लाइंट को लौटाना न चाहें, जैसे पासवर्ड।

Query टाइप वह प्रकार परिभाषित करता है जिन क्वेरीज़ को हमारी API के भीतर निष्पादित किया जा सकता है। प्रत्येक क्वेरी एक User या User की एक array लौटाएगी, लेकिन इनके बीच में हम इन क्वेरीज़ में विशिष्ट लॉजिक रख सकते हैं।

Mutation टाइप Query जैसा ही है, लेकिन इनका उद्देश्य डेटा में परिवर्तन करने वाली क्वेरीज़ होना है।

GraphQL टाइप डेफिनिशनों के होने के बाद, हम एप्लिकेशन के रिजॉल्वर लॉजिक पर ध्यान केंद्रित कर सकते हैं। प्रोजेक्ट की src/resolvers/index.ts फ़ाइल में, निम्नलिखित कोड शामिल करें:

import { UserService } from '../database/userService';
import { User, CreateUserInput, UpdateUserInput } from '../types';

export const resolvers = {
  Query: {
    users: async (): Promise<User[]> => {
      const userService = new UserService();
      return await userService.getAllUsers();
    },

    user: async (_: unknown, { id }: { id: string }): Promise<User | null> => {
      const userService = new UserService();
      return await userService.getUserById(id);
    },
  },

  Mutation: {
    createUser: async (
      _: unknown,
      { name, email, age }: CreateUserInput
    ): Promise<User> => {
      const userService = new UserService();
      return await userService.createUser({ name, email, age });
    },

    updateUser: async (
      _: unknown,
      { id, name, email, age }: UpdateUserInput
    ): Promise<User | null> => {
      const userService = new UserService();
      return await userService.updateUser({ id, name, email, age });
    },

    deleteUser: async (_: unknown, { id }: { id: string }): Promise<boolean> => {
      const userService = new UserService();
      return await userService.deleteUser(id);
    },
  },
};

ऊपर दिए गए रिजॉल्वर वही विस्तार हैं जो हमने क्वेरीज़ और म्यूटेशनों के लिए टाइप डेफिनिशन फ़ाइल में देखे थे।

users क्वेरी पर नज़र डालें:

users: async (): Promise<User[]> => {
  const userService = new UserService();
  return await userService.getAllUsers();
},

यह GraphQL क्वेरी हमारे UserService का उपयोग करेगी, जो डेटाबेस लॉजिक का हिस्सा है, सभी यूज़र्स प्राप्त करने के लिए। User[] प्रतिक्रिया यूज़र को भेजी जाएगी, लेकिन केवल वही हिस्से जो यूज़र ने अनुरोधित किए हैं और जो GraphQL टाइप डेफिनिशंस से मेल खाते हैं।

तो उदाहरण के लिए, मान लें कि क्लाइंट ने यह क्वेरी भेजी:

query {
	users {
		name
		email
	}
}

ऊपर के उदाहरण में हमारे पास काम करने के लिए और फ़ील्ड्स हैं, लेकिन क्लाइंट केवल name और email फ़ील्ड चाहता है। इसे उसी GraphQL टाइप डेफिनिशनों के आधार पर संभाला गया जिनके साथ क्लाइंट काम कर रहा है। Apollo यह सब संभव बनाने का जादू संभालता है।

यही नियम हमारी **src/resolvers/index.ts** फ़ाइल के भीतर हर क्वेरी और म्यूटेशन पर लागू होंगे।

createUser: async (
  _: unknown,
  { name, email, age }: CreateUserInput
): Promise<User> => {
  const userService = new UserService();
  return await userService.createUser({ name, email, age });
},

createUser टाइप डेफिनिशन के लिए, हमने देखा कि क्लाइंट name, email और age फ़ील्ड पास कर सकता है। createUser रिजॉल्वर फ़ंक्शन में, हम वही कर रहे हैं और अपने डेटाबेस सर्विस से createUser फ़ंक्शन को कॉल कर रहे हैं। प्रतिक्रिया क्लाइंट को उन्हीं फ़ील्ड्स के आधार पर लौटाई जाती है जिन्हें उन्होंने अनुरोध किया है।

GraphQL API को बिल्ड और रन करें

इस बिंदु पर, GraphQL API अच्छी अवस्था में होनी चाहिए। इस एप्लिकेशन को बिल्ड और रन करने के कई तरीके हैं।

उदाहरण के लिए, आप अपने **package.json** फ़ाइल में निम्नलिखित जोड़ सकते हैं:

"scripts": {
	"build": "tsc",
	"start": "node dist/index.js",
	"dev": "tsx src/index.ts",
	"dev:watch": "tsx watch src/index.ts",
	"watch": "tsc -w"
},

यदि आपने यह तरीका चुना है, तो आप प्रोजेक्ट को बिल्ड करने के लिए npm run build चला सकते हैं और प्रोजेक्ट को सर्व करने के लिए npm run start। डिफ़ॉल्ट पोर्ट 3000 है, और इसे cURL या आपकी पसंद के किसी भी टूल से टेस्ट किया जा सकता है।

निष्कर्ष

आपने अभी देखा कि कैसे TypeScript और MongoDB को डेटाबेस के रूप में उपयोग करके एक GraphQL API बनाई जाती है। जबकि यह उदाहरण सरल रखने के लिए बनाया गया था, हमने देखा कि अपनी डेटाबेस पर CRUD ऑपरेशन करने के लिए क्वेरीज़ और म्यूटेशंस कैसे बनाते हैं। GraphQL API में MongoDB एक बेहतरीन मेल है, क्योंकि जिस डेटा के साथ आप काम कर रहे हैं, उसकी संरचना शुरुआत से अंत तक मिलती-जुलती रहती है। उदाहरण के लिए, क्लाइंट ऐसी क्वेरी चलाता है जो JSON जैसी दिखती है। क्वेरी के माध्यम से पास किया गया डेटा बहुत कम फ़ॉर्मैटिंग और समायोजन के साथ उपयोग किया जा सकता है, और डेटाबेस को भेजते समय और वहाँ से प्राप्त करते समय भी यही बात लागू होती है। अनुभव सुचारु रहता है, जिससे आप डेटाबेस की आवश्यकताओं के अनुकूल डेटा हेरफेर की बजाय उपयोगकर्ता अनुभव पर अधिक ध्यान केंद्रित कर सकते हैं।

यदि आप इस ट्यूटोरियल में किसी भी बिंदु पर अटक गए थे, तो GitHub पर तैयार प्रोजेक्ट ज़रूर देखें।

FAQs

मुझे GraphQL का उपयोग क्यों करना चाहिए?

GraphQL बढ़िया है क्योंकि आप API के विरुद्ध वैसे ही क्वेरी कर सकते हैं जैसे किसी डेटाबेस के साथ करते हैं, और बदले में केवल उतना ही माँगते हैं जितनी आपको आवश्यकता है। इससे क्लाइंट के लिए चीज़ें हल्की और कुशल बनती हैं।

क्या GraphQL के लिए Apollo ही एकमात्र विकल्प है?

Apollo GraphQL API बनाना बहुत आसान बना देता है, लेकिन यह एकमात्र विकल्प नहीं है। हालांकि, अन्य विकल्प इस ट्यूटोरियल के दायरे से बाहर हैं।

GraphQL के साथ MongoDB क्यों उपयोग करें?

दोनों JSON-जैसे डेटा फ़ॉर्मैट्स के साथ काम करते हैं, इसलिए सूचना जब डेटाबेस से API होते हुए क्लाइंट तक बहती है, तब आपको अलग-अलग डेटा संरचनाओं के बीच लगातार रूपांतरण नहीं करना पड़ता।

रिजॉल्वर क्या करता है?

रिजॉल्वर एक फ़ंक्शन है जो GraphQL को बताता है कि आपकी स्कीमा में किसी विशेष फ़ील्ड के लिए डेटा कैसे प्राप्त या संशोधित करना है।

क्वेरी और म्यूटेशन में क्या अंतर है?

क्वेरीज़ डेटा पढ़ने/लाने के लिए होती हैं (जैसे GET अनुरोध), जबकि म्यूटेशंस डेटा बनाने, अपडेट करने या हटाने के लिए होती हैं (जैसे POST, PUT, DELETE)।


Nic Raboy's photo
Author
Nic Raboy

निक रैबॉय MongoDB में डेवलपर रिलेशंस लीड हैं, जहाँ वे Python, Java, C# और PHP डेवलपर्स की एक टीम का नेतृत्व करते हैं जो ऐसा शानदार कंटेंट बनाते हैं जिससे डेवलपर्स अपने प्रोजेक्ट्स में MongoDB को सफलतापूर्वक शामिल कर सकें। उन्हें Golang और JavaScript का अनुभव है और वे प्रायः अपने विकास से जुड़ी अनेक यात्राओं के बारे में लिखते हैं।

विषय
MongoDB

शीर्ष DataCamp पाठ्यक्रम

कोर्स

Python में MongoDB परिचय

3 घंटा
24.4K
MongoDB के साथ लचीले ढंग से संरचित डेटा को हेरफेर और विश्लेषण करना सीखें।
विवरण देखेंRight Arrow
पाठ्यक्रम शुरू करें

कोर्स

डेटा इनसाइट्स को प्रभावी ढंग से संप्रेषित करना

2 घंटा
88.2K
डेटा-आधारित संगठन लगातार अंतर्दृष्टियों पर निर्भर रहते हैं ताकि कार्रवाई को प्रेरित किया जा सके और बदलाव लाया जा सके।
और देखेंRight Arrow