Use public fields in MJAPI
Using the @Public Directive for Unauthenticated Access
@Public Directive for Unauthenticated AccessThe @Public directive in our system enables unauthenticated access to specific fields within our GraphQL schema. This feature is particularly useful for exposing public-facing data or allowing anonymous users to fetch certain information. In this article, we'll go through how to use the @Public directive, provide code examples, and offer best practices to avoid common errors.
What is the @Public Directive?
@Public Directive?The @Public directive is a GraphQL decorator that allows specific fields or operations to bypass authentication. By default, all fields in the system require authentication unless explicitly marked with the @Public decorator. For complex object types, each child field also needs to be independently marked with @Public to avoid authentication errors. This mechanism provides flexibility in building public endpoints for metadata or general information.
Using @Public in Custom Resolvers
@Public in Custom ResolversTo use the @Public directive, you need to apply it to the relevant fields in your custom resolver. Below is an example of how to use @Public with a custom ColorResolver:
import { AppContext } from '../types.js';
import { Ctx, Field, Int, ObjectType, PubSub, PubSubEngine, Query, Resolver, Root, Subscription } from 'type-graphql';
import { Public } from '../directives/index.js';
@ObjectType()
export class Color {
@Field(() => Int)
@Public()
ID: number;
@Field(() => String)
@Public()
name: string;
@Field(() => String)
@Public()
createdZ: string;
}
@ObjectType()
export class ColorNotification {
@Public()
@Field(() => String, { nullable: true })
message?: string;
@Public()
@Field(() => Date)
date!: Date;
}
export interface ColorNotificationPayload {
message?: string;
}
@Resolver(Color)
export class ColorResolver {
@Subscription(() => ColorNotification, { topics: 'COLOR' })
@Public()
colorSubscription(@Root() { message }: ColorNotificationPayload): ColorNotification {
return { message, date: new Date() };
}
@Query(() => [Color])
@Public()
async colors(@Ctx() _ctx: AppContext, @PubSub() pubSub: PubSubEngine) {
const createdZ = new Date().toISOString();
pubSub.publish('COLOR', {
message: 'Colors were requested!',
});
return [
{ ID: 1, name: 'Red', createdZ },
{ ID: 2, name: 'Orange', createdZ },
{ ID: 3, name: 'Yellow', createdZ },
{ ID: 4, name: 'Green', createdZ },
{ ID: 5, name: 'Blue', createdZ },
{ ID: 6, name: 'Purple', createdZ },
];
}
}Explanation
In this example:
- The
ColorandColorNotificationclasses define GraphQL object types with fields marked by the@Publicdecorator. This allows these fields (ID,name,createdZ,message, anddate) to be accessed without requiring authentication. - The
ColorResolverclass contains two operations:colorSubscription: A subscription to theCOLORtopic that publishes messages and dates. It is marked with@Public(), making it accessible to unauthenticated users.colors: A query that returns a list of colors, marked with@Public(), allowing public access to fetch this information.
By using the @Public() decorator, you expose specific fields and operations without requiring the user to log in. However, it's crucial to ensure that every field and child field that you want to be publicly accessible has the @Public decorator to avoid unexpected authentication errors.
Setting Up Custom Resolvers with @Public
@PublicWhen incorporating the @Public directive into your custom resolvers, remember the following:
- Add the
@PublicDecorator: Apply the@Publicdecorator to the fields, queries, or subscriptions that you want to be accessible without authentication. - Update the
[__mj].[Entity]Table: Ensure that theCustomResolverAPIvalue in the[__mj].[Entity]table is set appropriately (e.g., set it to1) to enable custom resolver functionality for the entity. - Mark Child Fields: For object types, mark each child field that should be publicly accessible with
@Public.
Accessing @Public Fields from an Angular App
@Public Fields from an Angular AppOnce you’ve set up @Public fields in your custom resolver, you can interact with them in your Angular app. Use the GraphQLDataProvider's ExecuteGQL method to run queries and mutations:
this.graphQLDataProvider.ExecuteGQL({
query: /* graphql */ `
query {
colors {
ID
name
createdZ
}
}
`,
});In this example, the query fetches data from the colors field, which is marked with the @Public decorator in the ColorResolver. This enables unauthenticated access to this data.
Common Errors and Best Practices
Unauthenticated Errors
If you encounter an "unauthenticated" error:
- Check that the
@Publicdecorator is correctly applied to all fields you want to expose. - Ensure that nested fields (child fields) in object types also have the
@Publicdirective if they should be accessible without authentication. - Remove the
Authorizationheader when accessing public fields. If you include an invalid or expired token, the system will still attempt to authenticate and fail.
Manual API Clients
When using manual API clients (e.g., Postman), make sure to manually adjust headers to exclude Authorization when interacting with @Public fields.
Conclusion
The @Public directive is a powerful tool for creating public-facing endpoints in your GraphQL schema. By marking fields, queries, and subscriptions with the @Public decorator, you allow unauthenticated access while retaining the flexibility to restrict access to sensitive information. When using @Public:
- Apply the directive to every field that should be accessible without authentication.
- Mark each child field in object types as necessary.
- Avoid setting the
Authorizationheader if you don't have a valid token.
By following these guidelines, you can effectively use the @Public directive to manage public access in your application.
Updated 7 months ago
