Nhost
Nhost BaaS with Hasura GraphQL, PostgreSQL, authentication, and storage
You are an expert in Nhost for rapid backend development, including its Hasura GraphQL engine, PostgreSQL database, authentication, storage, and serverless functions. ## Key Points - Hasura Console: http://localhost:1337 - GraphQL endpoint: http://localhost:1337/v1/graphql - Auth: http://localhost:1337/v1/auth - Storage: http://localhost:1337/v1/storage - role: user - role: user - role: user - role: user - Use Hasura metadata files to version-control your schema, permissions, and relationships — run `nhost metadata export` to sync. - Configure row-level permissions in Hasura using the `X-Hasura-User-Id` session variable, which Nhost Auth injects automatically. - Use GraphQL subscriptions sparingly; they are powerful for real-time features but keep open WebSocket connections. - Leverage Hasura's event triggers to call serverless functions on database events instead of polling. ## Quick Example ```bash npm install @nhost/nhost-js ``` ```bash npm install @nhost/react @nhost/react-apollo ```
skilldb get baas-skills/NhostFull skill: 310 linesNhost — Backend as a Service
You are an expert in Nhost for rapid backend development, including its Hasura GraphQL engine, PostgreSQL database, authentication, storage, and serverless functions.
Core Philosophy
Overview
Nhost is an open-source backend platform that bundles Hasura (instant GraphQL and REST APIs over PostgreSQL), built-in authentication (email, OAuth, magic link, WebAuthn), S3-compatible file storage with image transformations, and serverless functions. It provides a Firebase-like developer experience on top of a Postgres + GraphQL stack. Nhost can be self-hosted or used as a managed cloud service.
Setup & Configuration
CLI Setup
# Install Nhost CLI
npm install -g nhost
# Create a new project
nhost init
# Start local development environment (Postgres, Hasura, Auth, Storage)
nhost up
Local services:
- Hasura Console: http://localhost:1337
- GraphQL endpoint: http://localhost:1337/v1/graphql
- Auth: http://localhost:1337/v1/auth
- Storage: http://localhost:1337/v1/storage
Client SDK
npm install @nhost/nhost-js
import { NhostClient } from '@nhost/nhost-js';
const nhost = new NhostClient({
subdomain: '<project-subdomain>', // or backendUrl for self-hosted
region: '<project-region>',
});
React Integration
npm install @nhost/react @nhost/react-apollo
import { NhostProvider } from '@nhost/react';
import { NhostApolloProvider } from '@nhost/react-apollo';
function App() {
return (
<NhostProvider nhost={nhost}>
<NhostApolloProvider nhost={nhost}>
<MainApp />
</NhostApolloProvider>
</NhostProvider>
);
}
Core Patterns
Authentication
// Sign up
const { session, error } = await nhost.auth.signUp({
email: 'user@example.com',
password: 'secure-password',
options: {
displayName: 'Alice',
defaultRole: 'user',
},
});
// Sign in
const { session, error } = await nhost.auth.signIn({
email: 'user@example.com',
password: 'secure-password',
});
// OAuth (e.g., GitHub)
nhost.auth.signIn({ provider: 'github' });
// Magic link
await nhost.auth.signIn({ email: 'user@example.com' });
// Get current user
const user = nhost.auth.getUser();
// Sign out
await nhost.auth.signOut();
// React hooks
import { useUserData, useSignInEmailPassword, useAuthenticated } from '@nhost/react';
function Profile() {
const user = useUserData();
const isAuthenticated = useAuthenticated();
return isAuthenticated ? <p>Hello {user.displayName}</p> : <p>Not logged in</p>;
}
GraphQL Queries (via Hasura)
import { useQuery, useMutation, useSubscription } from '@apollo/client';
import { gql } from '@apollo/client';
// Query
const GET_POSTS = gql`
query GetPosts($limit: Int!) {
posts(
where: { published: { _eq: true } }
order_by: { created_at: desc }
limit: $limit
) {
id
title
body
author {
display_name
}
created_at
}
}
`;
function PostList() {
const { data, loading, error } = useQuery(GET_POSTS, {
variables: { limit: 10 },
});
if (loading) return <p>Loading...</p>;
return data.posts.map((post) => <div key={post.id}>{post.title}</div>);
}
// Mutation
const CREATE_POST = gql`
mutation CreatePost($title: String!, $body: String!) {
insert_posts_one(object: { title: $title, body: $body }) {
id
title
}
}
`;
function CreatePost() {
const [createPost] = useMutation(CREATE_POST);
const handleCreate = async () => {
await createPost({
variables: { title: 'New Post', body: 'Content' },
});
};
return <button onClick={handleCreate}>Create</button>;
}
// Real-time subscription
const POSTS_SUBSCRIPTION = gql`
subscription OnNewPosts {
posts(order_by: { created_at: desc }, limit: 10) {
id
title
created_at
}
}
`;
function LivePosts() {
const { data } = useSubscription(POSTS_SUBSCRIPTION);
return data?.posts.map((post) => <div key={post.id}>{post.title}</div>);
}
Hasura Permissions
Permissions are configured per role per table in the Hasura Console or via metadata:
# nhost/metadata/databases/default/tables/public_posts.yaml
table:
name: posts
schema: public
select_permissions:
- role: user
permission:
columns: [id, title, body, created_at, author_id]
filter:
_or:
- published: { _eq: true }
- author_id: { _eq: X-Hasura-User-Id }
insert_permissions:
- role: user
permission:
columns: [title, body]
set:
author_id: X-Hasura-User-Id
check:
author_id: { _eq: X-Hasura-User-Id }
update_permissions:
- role: user
permission:
columns: [title, body, published]
filter:
author_id: { _eq: X-Hasura-User-Id }
delete_permissions:
- role: user
permission:
filter:
author_id: { _eq: X-Hasura-User-Id }
Storage
// Upload a file
const { fileMetadata, error } = await nhost.storage.upload({
file: fileObject,
bucketId: 'default',
});
// Get file URL (public bucket)
const url = nhost.storage.getPublicUrl({ fileId: fileMetadata.id });
// Get presigned URL (private bucket)
const { presignedUrl } = await nhost.storage.getPresignedUrl({
fileId: fileMetadata.id,
});
// Get image with transformations
const url = nhost.storage.getPublicUrl({
fileId: fileMetadata.id,
width: 400,
height: 400,
quality: 80,
});
// Delete a file
await nhost.storage.delete({ fileId: fileMetadata.id });
Serverless Functions
// nhost/functions/hello.ts
import { Request, Response } from 'express';
export default (req: Request, res: Response) => {
res.json({ message: 'Hello from Nhost Functions!' });
};
// nhost/functions/webhook/post-created.ts
import { Request, Response } from 'express';
export default async (req: Request, res: Response) => {
const { event } = req.body;
const post = event.data.new;
// React to post creation (send notification, update search index, etc.)
console.log('New post created:', post.title);
res.status(200).json({ success: true });
};
Functions are automatically deployed at /v1/functions/<path>.
Best Practices
- Use Hasura metadata files to version-control your schema, permissions, and relationships — run
nhost metadata exportto sync. - Configure row-level permissions in Hasura using the
X-Hasura-User-Idsession variable, which Nhost Auth injects automatically. - Use GraphQL subscriptions sparingly; they are powerful for real-time features but keep open WebSocket connections.
- Leverage Hasura's event triggers to call serverless functions on database events instead of polling.
- Use Nhost's built-in email templates for verification, password reset, and magic link emails.
Common Pitfalls
- Hasura permission gaps: Every table needs permissions defined per role; tables without permissions return empty results (not errors), which is confusing.
- Not applying migrations: Local schema changes must be captured as migrations (
nhost migrations create) to deploy to production. - Session management in SSR: For server-side rendering, use the Nhost SDK's
nhostSessionMiddlewareor pass tokens explicitly. - Storage bucket permissions: Default bucket may be private; configure bucket permissions in the dashboard for public assets.
- Over-fetching with GraphQL: Request only the fields you need — Hasura generates efficient SQL, but bloated queries with deep relations still impact performance.
Anti-Patterns
Over-engineering for hypothetical requirements. Building for scenarios that may never materialize adds complexity without value. Solve the problem in front of you first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide wastes time and introduces risk.
Premature abstraction. Creating elaborate frameworks before having enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at system boundaries. Internal code can trust its inputs, but boundaries with external systems require defensive validation.
Skipping documentation. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add baas-skills
Related Skills
Appwrite
Appwrite self-hosted BaaS with database, auth, storage, and serverless functions
AWS Amplify
AWS Amplify BaaS with AppSync GraphQL, Cognito auth, S3 storage, and Lambda functions
Backendless
Backendless BaaS with real-time database, user authentication, Cloud Code,
Clerk Auth
Clerk authentication service with pre-built UI components, session management, and multi-framework support
Convex
Convex real-time backend with reactive queries, mutations, and serverless functions
Encore
Encore is a backend development platform that automatically provisions, configures, and manages cloud infrastructure based on your Go code. It simplifies building and deploying cloud-native applications by allowing you to focus purely on business logic.