NestJS module for user authentication and management using Stack Auth.
yarn add @stack-auth/nestjs
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { StackAuthModule } from '@stack-auth/nestjs';
@Module({
imports: [
ConfigModule.forRoot(),
StackAuthModule.registerAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
baseURL: configService.getOrThrow('STACKAUTH_BASE_URL'),
stackAuth: {
projectId: configService.getOrThrow('STACKAUTH_PROJECT_ID'),
accessType: configService.getOrThrow('STACKAUTH_ACCESS_TYPE'),
// For client mode
publishableClientKey: configService.get('STACKAUTH_PUBLISHABLE_CLIENT_KEY'),
// For server mode
secretServerKey: configService.get('STACKAUTH_SECRET_SERVER_KEY'),
},
}),
}),
],
})
export class AppModule {}
# .env
STACKAUTH_BASE_URL=https://api.stackauth.com
STACKAUTH_PROJECT_ID=your-project-id
STACKAUTH_ACCESS_TYPE=server # or client
STACKAUTH_SECRET_SERVER_KEY=sk_test_123 # only for server mode
STACKAUTH_PUBLISHABLE_CLIENT_KEY=pk_test_123 # only for client mode
// users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectStackAuthRepository } from '@stack-auth/nestjs';
import { User } from '@stack-auth/nestjs/models';
@Injectable()
export class UsersService {
constructor(
@InjectStackAuthRepository('server.users')
private readonly usersRepo: UsersServerService,
) {}
// Get
async getUser(userId: string): Promise<User> {
const user = await this.usersRepo.getUser(userId);
return user;
}
// Update profile
async updateProfile(userId: string, displayName: string, email: string): Promise<User> {
const user = await this.usersRepo.getUser(userId);
return user.update({
display_name: displayName,
primary_email: email,
});
}
// Create user
async createUser(data: { primary_email: string; display_name?: string }): Promise<User> {
return this.usersRepo.create(data);
}
// List users
async listUsers(params?: { limit?: number; cursor?: string; query?: string; team_id?: string; order_by?: string; desc?: boolean }) {
return this.usersRepo.list(params);
}
// Delete user
async deleteUser(userId: string): Promise<boolean> {
return this.usersRepo
.delete(userId)
.then((response) => response.success)
.catch(() => false);
}
}
// users.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { InjectStackAuthRepository } from '@stack-auth/nestjs';
import { User } from './entities/user.entity';
@Resolver(() => User)
export class UsersResolver {
constructor(
@InjectStackAuthRepository('server.users')
private readonly usersRepo: UsersServerService,
) {}
@Query(() => User)
async user(@Args('id') id: string) {
return this.usersRepo.getUser(id);
}
@Mutation(() => User)
async updateUser(@Args('id') id: string, @Args('input') input: UpdateUserInput) {
const user = await this.usersRepo.getUser(id);
return user.update(input);
}
}
// users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { InjectStackAuthRepository } from '@stack-auth/nestjs';
@Controller('users')
export class UsersController {
constructor(
@InjectStackAuthRepository('server.users')
private readonly usersRepo: UsersServerService,
) {}
@Get(':id')
async getUser(@Param('id') id: string) {
return this.usersRepo.getUser(id);
}
@Post()
async createUser(@Body() data: CreateUserDto) {
return this.usersRepo.create(data);
}
}
Note: When using the
@InjectStackAuthRepository()
decorator without parameters, you will have access to all repositories (client and server) through the injected service.
Name | Injection |
---|---|
Users | @InjectStackAuthRepository('server.users') |
Sessions | @InjectStackAuthRepository('server.sessions') |
Name | Injection |
---|---|
Users | @InjectStackAuthRepository('client.users') |
The project uses a branch-based workflow to ensure quality and stability:
dev -> main
↑ ↑
| (production)
|
(development)
dev
: Pre-release version with tested featuresmain
: Stable production version
Each branch has its own NPM tag:
# Stable version (main)
npm install @stackauth/nestjs@latest
# Pre-release version (develop)
npm install @stackauth/nestjs@next
- Make changes to
dev
via PR - When ready for production, merge to
main
via PR
This project follows Semantic Versioning using Semantic Release. The version numbers are automatically managed based on commit messages.
Each merge in any branch automatically generates a new version with the appropriate prefix:
dev
:1.0.0-dev.1
main
:1.0.0
Each commit message consists of a header, a body and a footer:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
feat
: A new feature (triggers a MINOR version bump)fix
: A bug fix (triggers a PATCH version bump)perf
: A code change that improves performancerefactor
: A code change that neither fixes a bug nor adds a featurestyle
: Changes that do not affect the meaning of the codetest
: Adding missing tests or correcting existing testsdocs
: Documentation only changeschore
: Changes to the build process or auxiliary toolsci
: Changes to CI configuration files and scriptsrevert
: Reverts a previous commit
- Adding
BREAKING CHANGE:
in the commit footer will trigger a MAJOR version bump - Example:
feat(auth): change authentication API
BREAKING CHANGE: The authentication API has been completely revamped.
Previous methods will no longer work.
- MAJOR version (1.0.0 → 2.0.0)
- When making incompatible API changes
- Triggered by
BREAKING CHANGE:
in commit message
- MINOR version (1.0.0 → 1.1.0)
- When adding functionality in a backwards compatible manner
- Triggered by
feat:
commits
- PATCH version (1.0.0 → 1.0.1)
- When making backwards compatible bug fixes
- Triggered by
fix:
commits
# Minor Feature Release (1.0.0 → 1.1.0)
git commit -m "feat(users): add email verification endpoint"
# Patch Release (1.0.0 → 1.0.1)
git commit -m "fix(auth): resolve token expiration issue"
# Major Release with Breaking Change (1.0.0 → 2.0.0)
git commit -m "feat(auth): implement OAuth2 authentication
BREAKING CHANGE: Previous authentication methods are deprecated.
Users need to migrate to OAuth2."
# No Version Change
git commit -m "docs: update API documentation"
git commit -m "style: format code according to new rules"
git commit -m "test: add unit tests for user service"
To test your application with the module:
describe('AppModule', () => {
let module: TestingModule;
beforeEach(async () => {
module = await Test.createTestingModule({
imports: [
StackAuthModule.register({
baseURL: 'http://test.com',
stackAuth: {
projectId: 'test-project',
accessType: 'server',
secretServerKey: 'sk_test_123',
},
}),
],
}).compile();
});
it('should be defined', () => {
expect(module).toBeDefined();
});
});
MIT