The Open Source Firebase Alternative with GraphQL

Last update: Jun 29, 2022

Nhost


Nhost

Quickstart   •   Website   •   Docs   •   Blog   •   Discord   •   Twitter

What is Nhost?

Nhost is a modern open-source Firebase alternative with the same developer experience on the backend as Netlify and Vercel on the frontend.

Nhost's goal is to provide the most productive developer experience to build apps. We use the most popular and powerful technologies and make them easy to use with zero vendor lock-in.

We also provide a workflow from your local development, to staging (coming soon), and finally to production. We try to make your life as a developer as efficient as possible.

A Nhost backend includes a PostgreSQL database, a GraphQL API (with Hasura), Authentication, Storage, and Serverless Functions.

Getting started

Create a Nhost project for free and get your backend in less than a minute.

Start your Nhost project →

How Nhost works

The Nhost stack consists of:

Database PostgreSQL

  • The World's Most Advanced Open Source Relational Database.

Realtime GraphQL (Hasura's GraphQL Engine)

  • Instant GraphQL API based on your database schema.
  • Event trigger webhooks on database changes (insert / update / delete).
  • Connect remote GraphQL schemas.
  • Hasura Actions to extend the GraphQL API with custom business logic.
  • Powerful permission system based on JWT tokens.

Authentication (Hasura Backend Plus)

  • Email / Password.
  • OAuth providers (Google, GitHub, Facebook, Twitter, Apple, Spotify, LinkedIn, Windows Live).

Storage (Hasura Backend Plus)

  • Let your users upload and download files / documents / images.
  • S3 like storage API.
  • On-the-fly image transformation.

Custom API / serverless functions

  • Add any business logic to your backend.

Community

Use Nhost GitHub Discussions and join our Discord Server.

GitHub

https://github.com/nhost/nhost
Comments
  • 1. Started getting error - Can't import the named export 'ApolloProvider' from non EcmaScript module

    I've started getting this error while compiling suddenly when updated the Nhost dependencies

    The version for Nhost libraries are - "@nhost/nhost-js": "^1.2.1", "@nhost/react": "^0.7.10", "@nhost/react-apollo": "^4.2.11",

    ./node_modules/@nhost/react-apollo/dist/index.esm.mjs Can't import the named export 'ApolloProvider' from non EcmaScript module (only default export is available)

    Tried with downgrading and install the dependencies but the issue is not resolved and now the my portal is broken. "@nhost/nhost-js": "^1.1.14", "@nhost/react": "^0.7.8", "@nhost/react-apollo": "^4.2.9",

    Reviewed by dikshatekriwal at 2022-06-01 11:45
  • 2. `hasura-auth-js` breaking on Vercel

    While deploying my NuxtJS application on Vercel, the hasura-auth-js is breaking for some reason. This wasn't happening until a month ago. And I didn't even change anything on my end. I'm also not getting this error locally. Everything is working fine locally.

    I've tried changing multiple versions of @nhost/nhost-js. But none of them could solve this issue. Even tried falling back on v0.3.12. Even that didn't work.

    Something is happening in @nhost/hasura-auth-js. Something that wasn't happening a month before.

    Maybe @plmercereau can take a look at this.

    ERROR in ./node_modules_dev/@nhost/hasura-auth-js/dist/index.cjs.js 1:8801
      Module parse failed: Unexpected token (1:8801)
      You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
      > var A=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames,p=Object.getOwnPropertySymbols;var y=Object.prototype.hasOwnProperty,I=Object.prototype.propertyIsEnumerable;var P=(i,e,n)=>e in i?A(i,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):i[e]=n,d=(i,e)=>{for(var n in e||(e={}))y.call(e,n)&&P(i,n,e[n]);if(p)for(var n of p(e))I.call(e,n)&&P(i,n,e[n]);return i};var N=i=>A(i,"__esModule",{value:!0});var T=(i,e)=>{var n={};for(var t in i)y.call(i,t)&&e.indexOf(t)<0&&(n[t]=i[t]);if(i!=null&&p)for(var t of p(i))e.indexOf(t)<0&&I.call(i,t)&&(n[t]=i[t]);return n};var x=(i,e)=>{for(var n in e)A(i,n,{get:e[n],enumerable:!0})},k=(i,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of U(e))!y.call(i,r)&&(n||r!=="default")&&A(i,r,{get:()=>e[r],enumerable:!(t=O(e,r))||t.enumerable});return i};var D=(i=>(e,n)=>i&&i.get(e)||(n=k(N({}),e,1),i&&i.set(e,n),n))(typeof WeakMap!="undefined"?new WeakMap:0);var L={};x(L,{HasuraAuthClient:()=>C});var m=require("xstate"),o=require("@nhost/core");var E=require("@nhost/core"),g=()=>typeof window<"u",c=i=>!i||!i.accessToken.value||!i.refreshToken.value?null:{accessToken:i.accessToken.value,accessTokenExpiresIn:(i.accessToken.expiresAt.getTime()-Date.now())/1e3,refreshToken:i.refreshToken.value,user:i.user},l=(i,e)=>{e.forEach(n=>{typeof i[n]!="function"&&console.error(`clientStorage.${n} is not a function`)})},w=(i,e)=>{if(e){if(i==="react-native"||i==="custom")return l(e,["getItem"]),n=>{var t;return(t=e.getItem)==null?void 0:t.call(e,n)};if(i==="capacitor")return l(e,["get"]),n=>{var t;return(t=e.get)==null?void 0:t.call(e,{key:n})};if(i==="expo-secure-storage")return l(e,["getItemAsync"]),n=>{var t;return(t=e.getItemAsync)==null?void 0:t.call(e,n)}}return E.defaultClientStorageGetter},R=(i,e)=>{if(e){if(i==="react-native"||i==="custom")return l(e,["setItem","removeItem"]),(n,t)=>{var r,s;t?(r=e.setItem)==null||r.call(e,n,t):(s=e.removeItem)==null||s.call(e,n)};if(i==="capacitor")return l(e,["set","remove"]),(n,t)=>{var r,s;t?(r=e.set)==null||r.call(e,{key:n,value:t}):(s=e.remove)==null||s.call(e,{key:n})};if(i==="expo-secure-storage")return l(e,["setItemAsync","deleteItemAsync"]),async(n,t)=>{var r,s;t?await((r=e.setItemAsync)==null?void 0:r.call(e,n,t)):(s=e.deleteItemAsync)==null||s.call(e,n)}}return E.defaultClientStorageSetter};var S={message:"User is already signed in",status:100},b={message:"User is not authenticated",status:101},G={message:"User is not anonymous",status:101},M={message:"Email needs verification",status:102},C=class{constructor({url:e,autoRefreshToken:n=!0,autoLogin:t=!0,clientStorage:r,clientStorageType:s="web",clientStorageGetter:u,clientStorageSetter:a,refreshIntervalTime:f,start:h=!0,Client:_=o.AuthClient}){this._client=new _({backendUrl:e,autoRefreshToken:n,autoSignIn:t,start:h,clientStorageGetter:u||w(s,r),clientStorageSetter:a||R(s,r),refreshIntervalTime:f})}async signUp(e){let n=await this.waitUntilReady(),{email:t,password:r,options:s}=e;return this.isAuthenticated()?{session:null,error:S}:new Promise(u=>{n.send({type:"SIGNUP_EMAIL_PASSWORD",email:t,password:r,options:s}),n.onTransition(a=>{if(a.matches({authentication:{signedOut:"needsEmailVerification"}}))return u({session:null,error:null});if(a.matches({authentication:{signedOut:"failed"}}))return u({session:null,error:a.context.errors.registration||null});if(a.matches({authentication:"signedIn"}))return u({session:c(a.context),error:null})})})}async signIn(e){let n=await this.waitUntilReady();if(this.isAuthenticated())return{session:null,mfa:null,error:S};if("provider"in e){let{provider:t,options:r}=e,s=(0,o.encodeQueryParameters)(`${this._client.backendUrl}/signin/provider/${t}`,(0,o.rewriteRedirectTo)(this._client.clientUrl,r));return g()&&(window.location.href=s),{providerUrl:s,provider:t,session:null,mfa:null,error:null}}return"email"in e&&"password"in e?new Promise(t=>{n.send(d({type:"SIGNIN_PASSWORD"},e)),n.onTransition(r=>{r.matches({authentication:"signedIn"})?t({session:c(r.context),mfa:null,error:null}):r.matches({authentication:{si
    message.txt
    5 KB
    

    Screenshot from Vercel deployment:

    Screen Shot 2022-04-15 at 08 35 11 AM

    Reviewed by mrinalwahal at 2022-04-16 00:59
  • 3. Inconsistent behaviour in the Authentication workflow due to background /auth/token API calls

    The Authentication workflow of Nhost SDK is not found stable due to background API calls which stores refresh token and token expiry time in localstorage. Once logged-in using nhost.auth.signIn function, the token gets expired in few minutes and the self called token api does not help to get the new token. Also, when trying to login again, the SDK shows this info in console {message: 'User is already signed in', status: 100} and the login function never gets called. Only after clearing localstorage manually and refreshing the page, it allows to call the login function. All other function (signup) stops working and throws the same info in console if login is failed.

    I've added a screen recording of the whole scenario explained above which will help to understand it better. https://www.awesomescreenshot.com/video/8364543?key=6d34d3f6ae94d9442a67bbda9364ee7c

    Reviewed by dikshatekriwal at 2022-04-12 09:35
  • 4. Calling auth from functions results in error

    This was working in previously but now after updating things to the nextjs package and using the node-js package for the functions it appears there is an issue in trying to call any of the auth endpoints....

    Specifically, we were needing to implement a more complicated flow around signup .. so this was implemented in a function endpoint hooked up to a Hasura action ...

    pretty straightforward...

    const creds: SignUpParams = {
                    email: params.email,
                    password: params.password ?? '',
                    options: {
                        displayName: params.display_name,
                        defaultRole: 'user',
                    },
                }
    
                const { error } = await nhost.auth.signUp(creds)
    
                if (error) {
                    console.log('error: an error occurred trying to create the account via normal endpoint')
                    return ApiResponses.handleApiError(res, error, error.message)
                }
    

    and then afterwards we hookup a bunch of stuff based on the proposed users role....

    So now after we hit the signUp call it just sits there and spins... and we eventually get this in the backend

    2022/03/30 17:12:34 http: proxy error: context canceled

    On the same instantiation of the sdk though the graphql endpoint is working .. I dunno..

    this is the current packages being used..

    image

    Thanks!

    Reviewed by ArieJones at 2022-03-30 23:00
  • 5. Bug - x-hasura-user-id is not accessible in production function

    Description

    Currently in local development, I am able to access the x-hasura-user-id in a custom function by forwarding client headers. I access the user id this way below locally and it works.

    const userId = req.body.session_variables["x-hasura-user-id"];
    

    Problem

    In the nhost production function, I am unable to access x-hasura-user-id. This is the error that was pulled from an nhost team member to help me debug:

    (unsaved) test event
    
    Response
    {
      "errorType": "Runtime.UnhandledPromiseRejection",
      "errorMessage": "TypeError: Cannot read property 'x-hasura-user-id' of undefined",
      "trace": [
        "Runtime.UnhandledPromiseRejection: TypeError: Cannot read property 'x-hasura-user-id' of undefined",
        "    at process.<anonymous> (/var/runtime/index.js:35:15)",
        "    at process.emit (events.js:400:28)",
        "    at processPromiseRejections (internal/process/promises.js:245:33)",
        "    at processTicksAndRejections (internal/process/task_queues.js:96:32)"
      ]
    

    Expectation

    I should be able to access the x-hasura-user-id from the session_variables the same way in production as I do in local development

    Reviewed by wontwon at 2021-11-25 06:52
  • 6. How to know what permissions a user have, through API?

    While building the UI, certain parts of UI need to know if user is allowed access or not.

    For example,

    • menu links enabled/disabled,
    • buttons enabled/disabled

    based on whether user is allowed to perform an action or not.

    This requires querying what permissions a user have for the underlying resource/table etc.

    Specifically, with the refine front-end to implement the access control provider we need a way to query what permissions a user has for a resource/action.

    Documentation specifies how to set the permissions but does not specify how to get the users' existing permissions.

    Any example that demonstrates how to achieve this functionality with nHost and Hasura backend permissions would be of great help.

    Reviewed by KrishnaPG at 2022-05-19 08:51
  • 7. feat: Auto-generate documentation from TSDoc

    This PR adds a CLI tool that can be used to auto-generate documentation from the TSDoc of our SDKs.

    Remaining tasks:

    • [x] Setup DocGen pre-commit hook
    • [x] DocGen configuration via JSON
    • [x] Check documentation structure
    • [x] Setup TypeDoc for all of the packages
    Reviewed by szilarddoro at 2022-04-25 07:40
  • 8. cannot quite workout how to use graphql.request with signed-in user

    I'm using nhost v2 with Vue3, and it's not quite clear how to use it properly. After user logged-in I'm keeping nhost session and client in pinia store. When (after login) there is nhost.graphql.request I see that it lacks authorization header, which obviously gives error (for tables where I've set permissions). [{"extensions": {"path": "$","code": "validation-failed"},"message": "no mutations exist"}] looking through methods I see there is setAccessToken, so I've set nhost.graphql.setAccessToken(nhost.auth.getSession().accessToken); And it worked after that. But there is no token refreshing, so it is stopping working after 15 minutes (default expiration time), [{"extensions": {"path": "$","code": "invalid-jwt"},"message": "Could not verify JWT: JWTExpired"}] it's not clear when and how to refresh token and why SDK doesn't handle it? What am I missing? I'm reading official docs and I've tried to update to latest SDK (npm update @nhost/nhost-js "up to date") could it be error on my side? (could I erase something when saving client/session in Pinia store?)

    Reviewed by yureckey at 2022-04-08 07:20
  • 9. The useUserData hook does not return the emailVerified or phoneNumberVerified user properties

    Hello Nhost,

    I'm using the useUserData hook to retrieve the current authenticated user data. Here's the properties I got by default:

    Screenshot 2022-03-30 at 09 14 22

    I'm wondering if we could include the emailVerified and the phoneNumberVerified properties to the return object? Or if we could have a way to configure it?

    One of the use cases I see with getting the emailVerified property is adjusting the UI based on whether or not the user is verified, like displaying a banner or blocking part of the application. It would be great to have this information right away from the useUserData hook and not have to perform an extra GraphQL query just to get that information.

    What do you think?

    Please let me know if you need more information to proceed with this request or would like to discuss it further.

    Thanks.

    Reviewed by gdangelo at 2022-03-30 07:22
  • 10. Custom email templates from the Nhost console

    In Hasura-auth, it is possible to define custom email templates, but it is not possible yet to configure and deploy them from the Nhost console or CLI. @alveshelio et al. would be pleased to get this feature. See this issue for follow-up.

    Reviewed by plmercereau at 2022-01-20 19:52
  • 11. JWT token security concerns

    From what i see here, the JWT token is stored in the localStorage.

    According to this article it should be stored in the sessionStorage instead to reduce the risk of an XSS attack by avoiding to store the token on the client persistently. As described on the Persisting sessions section of the article, it would break the functionality of persisting a session when relaunching the browser.

    There is also a mention about a fingerprint added to the token to mitigate the risk of token side-jacking. Is it implemented in hasura-auth or is there any plan to support it ?

    The sample project from hasura that implement thoses recommendations can be found here. You can find the code generating the fingerprint cookie for the user here and the fingerprint check for the refreshToken here

    Reviewed by sebpalluel at 2022-06-14 12:28
  • 12. Error when uploading multiple files

    The @nhost/react SDK throws the following error when trying to upload multiple files:

    index.esm.js?e606:263 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'error')
        at eval (index.esm.js?e606:263:1)
    

    which points to

    message: ((_b = response == null ? void 0 : response.data.error) == null ? void 0 : _b.message) || message
    

    I simplified the relevant code, but

    const { upload, add, clear } = useMultipleFilesUpload();
    
    async function handleFileUpload(event: ChangeEvent<HTMLInputElement>) {
      const {files} = event.target;
      const defaultBucket = { id: 'default' } // <- comes from the API normally, not relevant here
    
      // some validation ...
    
      try {
        setLoading(true)
    
        // Suggestion #1: Maybe this is something that we could improve as file input fields return FileLists instead of arrays containing a list of Files
        // ---
        // Suggestion #2: I'd say it's not always straightforward that users want to upload files this way.
        // I just want to upload it immediately after the `onChange` handler is fired by simply calling `upload` with the `FileList`.
        add(Array.from(files))
    
        // Suggestion #3:
        await upload({ bucketId: defaultBucket.id })
    
      } catch (error) {
        // showing some error to the users
      } finally {
        setLoading(false)
        event.target.value = null
        clear()
      }
    }
    

    I can even make my browser freeze from time to time after selecting 3-4 files. I also added a couple of suggestion as comments to the code. Perhaps we can improve the API by applying these changes.

    Reviewed by szilarddoro at 2022-06-28 15:01
  • 13. `nhostRefreshToken` cookie disappears

    I'm not sure why and when, but the nhostRefreshToken Cookie definitely disappears on mobile browsers even when the refreshToken is definitely valid (only a few hours old).

    I log every event of nhostClient.auth.onAuthStateChanged:

    let lastEvent = `init`
    let lastUser = null
    
    nhostClient.auth.onAuthStateChanged((event, session) => {
      if (typeof window !== 'undefined' && lastEvent !== event) {
        if (session?.user?.displayName) lastUser = session.user.displayName
        logger.info(
          { event, lastUser },
          `auth state ${lastEvent} => ${event}`
        )
        lastEvent = event
      }
    })
    

    This is what happening:

    • user logs in, gets a refreshToken, it is stored in a cookie
    • user leaves the app
    • after some time he opens the page again
    • the first auth event I can see in the logs is init => SIGNED_OUT (I guess the refreshToken is not valid anymore - but why?)
    • then the Cookie is not there anymore (I log the presence of Cookies.get('nhostRefreshToken') in the login page for debugging purposes).

    Any ideas? I'm using:

    "@nhost/apollo": "^0.5.17",
    "@nhost/nextjs": "^1.4.1",
    "@nhost/nhost-js": "^1.4.1",
    
    Reviewed by kratam at 2022-06-26 13:42
  • 14. `useAuthenticationStatus` on unstable network

    From Discord.

    If /v1/auth/token is not accessible, isLoading and isAuthenticated (from useAuthenticationStatus) are both false.

    This can cause a problem in combination with how many people protect their routes:

    import { useAuthenticationStatus } from '@nhost/react'
    import { Navigate, useLocation } from 'react-router-dom'
    
    const ProtectedRoute = ({ children }) => {
      const { isAuthenticated, isLoading } = useAuthenticationStatus()
      const location = useLocation()
    
      if (isLoading) {
        return (
          <div>Loading</div>
        )
      }
    
      if (!isAuthenticated) {
        return <Navigate to="/sign-in" state={{ from: location }} replace />
      }
    
      return children
    }
    
    export default ProtectedRoute
    

    If you're on a mobile phone, and the connection is not stable, it could be that our js SDK tries to sign in the user by making a request to /v1/auth/token. The endpoint is not reachable because of the bad connection from the mobile phone. So the SDK sets both isLoading and isAuthenticated to false, which will cause the user to be redirected to /sign-in.

    Here's what we could do:

    1. isLoading should only return false once the SDK is 100% sure about the isAuthenticated state. That means that isLoading should only be set to false if the SDK gets an actual response from /v1/auth/token. Either a 200 OK or 401 Unauthorize.
    2. If the SDK can't reach /v1/auth/token because the network connection is unstable, the SDK should retry the request every 5 seconds until it successfully gets a response.
    3. (not sure about this one): We can also introduce a new variable to useAuthenticationStatus indicating what's the reason for isLoading to be false. The reason will always be because the SDK is waiting for a network request, but it could be useful to inform if the network is stable and the SDK is making the first request, or if the network is unstable and the SDK is making the 10th request.
    Reviewed by elitan at 2022-06-26 06:10
An Open Source alternative to dbdiagram.io
An Open Source alternative to dbdiagram.io

dbdiagram-oss An Open Source alternative to dbdiagram.io, aiming to have the same basic features+more. Motivation behind the project was that $9/month

Jun 29, 2022
Jun 12, 2022
Nuxt.js Universal App with SSR via Firebase Functions and Firebase Hosting

Nuxt.js Universal App with SSR via Firebase Functions and Firebase Hosting Host a Nuxt Universal app or site by combining Nuxt.js with Firebase Cloud

Jun 24, 2022
Vue-firebase - New Project Firebase, BD no relacional

vue-firebase-start Project setup npm install Compiles and hot-reloads for development npm run serve Compiles and minifies for production npm run bui

Jan 3, 2022
Expensave is a MEVN + GraphQL web application which allows expense tracking over time.

Expensave Expensave is a MEVN + GraphQL web application created to track expenses over time. These expenses need to be added in form of items and are

Nov 14, 2021
🏎 A tiny and fast GraphQL client for Vue.js
🏎 A tiny and fast GraphQL client for Vue.js

villus Villus is a finger-like structures in the small intestine. They help to absorb digested food. A small and fast GraphQL client for Vue.js 3.x Th

Jun 27, 2022
A terminating Apollo Link for Apollo Client that allows FileList, File, Blob instances within query or mutation variables and sends GraphQL multipart requests

A terminating Apollo Link for Apollo Client that allows FileList, File, Blob instances within query or mutation variables and sends GraphQL multipart requests

Sep 1, 2020
Connect and play with Supabase REST API / Graphql easily
Connect and play with Supabase REST API / Graphql easily

Connect and play with Supabase REST API / Graphql easily

Jun 14, 2022
▶️ ViewTube is an alternative YouTube frontend.
▶️ ViewTube is an alternative YouTube frontend.

▶️ ViewTube is an alternative YouTube frontend.

Jun 26, 2022
DADT - Deniz AEM Dev Tools A alternative to AEM Chrome Plug-in
DADT - Deniz AEM Dev Tools A alternative to AEM Chrome Plug-in

DADT A alternative to AEM Chrome Plug-in Installation Download the production zip (aem-dev-tools-{version}-production.zip) from the release tab. And a

Oct 24, 2021
A Vite based alternative for @quasar/app

Quasar App Vite This package aims to be a Vite based alternative for @quasar/app. Install pnpm add -D @stefanvh/quasar-app-vite Example Clone the exa

Jun 23, 2022
Serverless LinkTree alternative that converts CSV file containing list of links in to LinkTree-like webpage.

CSV to Link Tree This is my personal project as a free alternative to LinkTree. And make the advantage of CSV file to add the links. Built on Gridsome

Nov 7, 2021
An alternative privacy-friendly YouTube frontend which is efficient by design.
An alternative privacy-friendly YouTube frontend which is efficient by design.

Piped An open-source alternative frontend for YouTube which is efficient by design. The Problem YouTube has an extremely invasive privacy policy which

Jul 3, 2022
Zinc Search engine. A lightweight alternative to elasticsearch that requires minimal resources, written in Go.
Zinc Search engine. A lightweight alternative to elasticsearch that requires minimal resources, written in Go.

Zinc Zinc is a search engine that does full text indexing. It is a lightweight alternative to elasticsearch and runs in less than 100 MB of RAM. It us

Jul 6, 2022
:star2: PJ Blog is an open source blog built with Laravel and Vue.js.
:star2: PJ Blog is an open source blog built with Laravel and Vue.js.

?? PJ Blog is an open source blog built with Laravel and Vue.js. https://pigjian.com Special thanks to the generous sponsorship by: PJ Blog This is a

Jun 29, 2022
Open source Web File Manager (Javascript + VueJS 2)
Open source Web File Manager (Javascript + VueJS 2)

Media Manager Media Manager is an open source web file manager and can be a nice alternative to Wordpress Media Manager, CKFinder, KCFinder, elFinder.

Jun 15, 2022
An open source tinder desktop client built with electron and Vue.js for educational purposes
An open source tinder desktop client built with electron and Vue.js for educational purposes

Flamme An open source cross-platform Tinder desktop client built with Electron and Vue.js for educational purposes. With analytical inspection of dail

Feb 13, 2022
An Open Source Static Site CMS Built With Love & Vue.
An Open Source Static Site CMS Built With Love & Vue.

Saleina CMS A CMS for static site generators. Give non-technical users a simple way to edit and add content to any site built with a static site gener

Dec 28, 2021