Node.js代码迁移至Apollo Server:业务逻辑适配Resolver咨询
Hey there! I’ve been in your exact situation when moving from Express routes to Apollo Server, so let’s walk through the most straightforward ways to reuse your existing user-handling and business logic in resolvers.
The Core Idea: Use Apollo’s context as Your "Req Object"
In Express, you pass data between middleware via req; in Apollo, the equivalent is the context object—it’s available to every resolver, making it perfect for sharing the user object across your GraphQL operations.
Step 1: Inject the User into Apollo Context
First, you’ll want to move your existing user-fetching logic into Apollo’s context setup. You can choose either of these options based on your existing code structure:
Option A: Reuse Your Existing Express Middleware
If you still want to leverage your original Express middleware to populate req.user, just mount Apollo after that middleware runs:
// Your existing Express middleware that sets req.user app.use(async (req, res, next) => { req.user = await fetchUserFromDatabaseOrToken(req); // Your existing logic next(); }); // Mount Apollo Server on the /graphql route app.use('/graphql', expressMiddleware(server));
Then, pass the pre-populated req.user into Apollo’s context:
const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => { // Now every resolver can access context.user return { user: req.user }; }, });
Option B: Move User Logic Directly into Context
If you want to cut out the Express middleware middleman, handle user fetching directly in the context function:
const server = new ApolloServer({ typeDefs, resolvers, context: async ({ req }) => { // Run your existing user-fetching logic here const user = await fetchUserFromDatabaseOrToken(req); return { user }; }, });
Step 2: Adjust Your Business Logic to Be Reusable
Your original createToken function probably depends on req.user. Let’s refactor it to accept a user parameter directly—this makes it usable in both Express and Apollo resolvers:
Before (Express-specific):
function createToken(req, res, next) { const token = generateAuthToken(req.user); res.send({ token }); next(); }
After (Reusable):
export function createToken(user) { return generateAuthToken(user); // Pure function, no dependency on req/res }
Step 3: Use the User in Resolvers
Now you can easily access context.user in any resolver and call your refactored business logic:
const resolvers = { Query: { currentUser: (_, __, context) => { // Return the user from context, just like you would from req.user return context.user; }, }, Mutation: { generateUserToken: (_, __, context) => { if (!context.user) { throw new Error("You need to be logged in to create a token"); } // Call your reusable createToken function const token = createToken(context.user); return { token, user: context.user }; }, }, };
Bonus: Handle Auth Checks with Custom Directives
If you have multiple resolvers that require a logged-in user (like your old Express auth middleware), you can create a custom GraphQL directive to avoid repeating code:
- Define the directive in your schema:
directive @requireAuth on FIELD_DEFINITION type Query { currentUser: User @requireAuth } type Mutation { generateUserToken: TokenPayload @requireAuth }
- Implement the directive logic:
const { SchemaDirectiveVisitor } = require('apollo-server-express'); const { defaultFieldResolver } = require('graphql'); class RequireAuthDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const originalResolver = field.resolve || defaultFieldResolver; field.resolve = async (...args) => { const context = args[2]; if (!context.user) { throw new Error("Authentication required for this operation"); } // Call the original resolver if the user is authenticated return originalResolver.apply(this, args); }; } }
- Add it to your Apollo Server config:
const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => ({ user: req.user }), schemaDirectives: { requireAuth: RequireAuthDirective, }, });
Now any field marked with @requireAuth will automatically check for a valid user in the context—no need to add the check to every resolver!
Key Takeaways
- Treat Apollo’s
contextlike your Expressreqobject for sharing data across resolvers - Refactor business logic to be pure functions (no req/res dependencies) for maximum reusability
- Use custom directives to replicate middleware-like behavior (auth checks, etc.) in GraphQL
内容的提问来源于stack exchange,提问作者JamesG

