What DBs does Redwood Support?

RedwoodJS uses Prisma2 as it’s GraphQL provider. Prisma2 is in development (see isprisma2ready) and currently supports relational databases, e.g. SQL.

Note: supporting NoSQL and other DBs, including serverless, is definitely on the Prisma roadmap.

Here is the current list:
link updated August 2020

1 Like

I’m curious as well! What about other database-like things, e.g. contentful, prismic, firebase, etc.?

Although I haven’t personally implemented each of those items in your list, this question gets to an important, powerful aspect of Redwood’s GraphQL API:

  • using a singular API, you are not limited to DBs — you can integrate with any other API including storage, services, etc.

Within the app, this means you’re not locked into just a “web/” side in Redwood. Conceivably you’ll be able to have “mobile/”, “desktop/”, etc. all within the monorepo and all using the singular “api/”.

And if you want to use Redwood’s GraphQL API to connect to AWS S3, send emails via Sendgrid, get content via Contentful, etc., you can do that, too.

We don’t have any examples yet, but I’d encourage people to give this a try and keep us posted on their progress and questions. RedwoodJS uses Apollo GraphQL, so that’s the place to start digging in.

2 Likes

I’ve started looking at Apollo Server and it advertises that you can combine multiple GraphQL APIs into the one. If I were to do custom API adapters, where would I start looking for how to do that in Redwood? I’ve not used Prisma before and got a bit confused with this part:

datasource DS {
  provider = "sqlite"
  url = env("DATABASE_URL")
}

generator photonjs {
  provider = "prisma-client-js"
  binaryTargets = env("BINARY_TARGET")
}

Since my app uses Firebase for user authentication and content managed via Prismic plus some other third party data sources (varying GraphQL and REST APIs) it’d be interesting to see how to stitch them all together within a unified Redwood API.

So the file you’re referencing above, schema.prisma, is specific to Prisma2, which is used by Redwood for SQL database support (as I mentioned in the first post of this topic). Basically you won’t work in the api/prisma/ directory. Instead you’ll be in the api/src/directory.

For something like this you will need to write your own SDL and Services (effectively resolver functions). The difference is that the functions in your services won’t talk to the database. For example, this is a snippet from the Tutorial api/src/services/posts/posts.js:

export const posts = () => {
  return db.post.findMany()
}

...

The db. above is specific to the database (via the Prisma2 setup), and this is the function behind the query for all posts in posts.sdl.js . In the case of integrating with Sendgrid to send mail, for example, the db. would instead be something like sendgrid.. And the function body would be specific to something like sending an email.

Also to note, just because our built-in support is currently for MySQL, PostgreSQL, and SQLite, there’s nothing blocking you from creating your own GraphQL queries, mutations, and resolvers for other databases like Firebase, MongoDB, Fauna, etc. :exploding_head:You just need to manually write the SDL and service functions.

There’s definitely some complexity here. But I hope you have enough to at least explore a bit and see what’s possible.

Some resources that might be of help to learn more about GraphQL specifically:

The idea of linking in multiple APIs into the same GraphQL schema gives me some strong Gatsby / Gridsome vibes. I wonder if it would be useful to add support to just drop Gatsby plugins (which already exist for sites like Contentful, etc) into Redwood, to provide access to these APIs without having to reimplement this thing again and again.

1 Like

Hi @sam Yes indeed some similar vibes :+1:

I’m not familiar with Gridsome, but for Gatsby I do know there’s a lot going on under the hood to implement GraphQL given the unique framework design. My suspicion is that there’d be redundant complexity in the way, effectively “wrapping a wrapper”.

Given our roadmap of core features we’ve yet to build for Redwood, I can’t foresee us getting to experiment with this any time soon. But we’d welcome a POC and can definitely offer feedback/direction along the way. Just keep us posted if that’s something you’re able to try.

1 Like

Wondering if anyone has tried to deploy a Redwood app with something aside from Heroku Postgres as detailed in the tutorial? I saw some discussion around deploying without a database and some limitations around AWS DBs.

I’m going to be working on building a Redwood project with Fauna over the next two weeks and was hoping to get some tips on any gotchas that might be coming at me. As far as I can tell there’s been a lot of talk about getting Redwood to work with NoSQL DBs but nothing concrete yet.

1 Like

Hey Anthony! I don’t know of too many other custom DB integrations besides this one: Using Redwood with Neo4j Database

At this time, and I’m guessing you know, Fauna is not supported by Prisma (yet). So you’ll have to setup your own connection, which is definitely possible. Please do start a Forum topic and keep us posted as you go – there’s a lot of interest in Fauna and I’m sure a lot of people will be curious to both help and learn from your experiment.

I know people are using Redwood currently with DigitalOcean and AWS. I haven’t heard of anyone using Azure DB or Google Cloud DB. But as long as it’s relational (and not MSQL) it is supported by Prisma.

Personally, I’m excited about MongoDB support from Prisma as I’m a big fan of the MongoDB Atlas service.

I tried integrating the tutorial blog post app with Azure for Postgres, at first glance it works well :slight_smile: Haven’t done anything prod-worthy with it, but at least I know it’s possible :smiley: I basically followed the Deployment section of the tutorial with a few Azure-specific notes here and there (thank you for writing such a well-detailed tutorial! :rocket:)

4 Likes

Nice! Thanks for the update and sounds like progress :rocket:

Curious if you tried (or if there’s an option) to configure anything in front of Azure Postgres for handling connection pooling/load? Here’s the RW related Doc: https://redwoodjs.com/docs/local-postgres-setup

I’m no expert, but the gist is that in a serverless environment the front-end connections could spin up an unlimited number of connections and slam the DB.

People using DigitalOcean have said PG Bouncer is working really well. On AWS is the Amazon RDS Proxy.

Does it look like Azure provides something similar?

Nothing broke when I added the connection_limit=1 parameter, but I haven’t tried opening multiple connections to the database either :woman_shrugging:

It doesn’t seem like Azure provides a service similar to Amazon RDS Proxy (yet). Actually, I found a tutorial for setting up PgBouncer for Azure DB for PostgreSQL :smiley: and Citus mentions it as their preferred connection pooler in this blog post, back from before they became part of Azure’s Postgres solutions.

Ah, good to know. And thanks for looking into it! I don’t think it’s needed right now by any means. But once Azure seems to be working for Redwood deploy then looking into connection pooling could be a good follow-on step.

Hope all’s well. And have a good weekend!

1 Like

Hi @ajcwebdev. Curious how Fauna + RW is working out?

I’ve given Fauna some thoughts and wonder if you have had the same.

RW’s web side talks to the api side over graphql. Cool.

The api side can talk to datastores via services + sdl. Also cool.

Out of the box, Prisma is supported since there is a readily available db client to import and write queries/mutations.

But, a service is a service. My SDL can define the queries, but I don’t have to use the Prisma db client. I could use the Contentful SDK – or call another graphql endpoint (Hasura … or Fauna … or Contentful for that matter) using graphql-request.

I’ve shared this example here before, but my I made an app a year or so ago where 8 year old niece uses Contentful to keep track of little Disney Princess cupcake characters she build from pieces of a board game. The SDL is:

import gql from 'graphql-tag'

export const schema = gql`
  type Cupcake {
    id: String!
    name: String!
    description: String!
    price: Float
    rating: Int
    slug: String!
    photos: [ContentfulAsset]
  }
  type Query {
    cupcakes: [Cupcake!]!
    cupcake(id: String!): Cupcake!
  }
`

and the service that uses their SDK is:

import { createClient } from 'contentful'

const client = createClient({
  space: process.env.CONTENTFUL_SPACE,
  accessToken: process.env.CONTENTFUL_DELIVERY_API_KEY,
})

const renderAsset = (fields) => {
  return { title: fields.title, file: fields.file }
}

const renderAssets = (assets) => {
  return assets.map((asset) => renderAsset(asset.fields))
}

const renderEntry = (entry) => {
  return {
    id: entry.sys.id,
    name: entry.fields.name,
    description: entry.fields.description,
    price: entry.fields.price,
    rating: entry.fields.rating,
    slug: entry.fields.slug,
    photos: renderAssets(entry.fields.photos),
  }
}

export const cupcakes = async () => {
  const response = await client.getEntries({
    content_type: 'cupcake',
    limit: 1000,
    order: 'fields.name',
  })

  return response.items.map((entry) => renderEntry(entry))
}

export const cupcake = async ({ id }) => {
  const entry = await client.getEntry(id)

  return renderEntry(entry)
}

and then I can use RW cells just as I would if I was querying a Prisma Postgres database:

import Cupcake from 'src/components/Cupcake'

export const QUERY = gql`
  query {
    cupcakes {
      id
      name
      description
      price
      rating
      slug
      photos {
        title
        file {
          url
        }
      }
    }
  }
`

export const Loading = () => {
  return (
    <Stack>
      <Box mb={6} maxW="sm" overflow="hidden">
        <Box p="6">
          <Skeleton height="20px" my="10px" />
        </Box>
      </Box>
      <Box mb={6} maxW="sm" overflow="hidden">
        <Box p="6">
          <Skeleton height="20px" my="10px" />
        </Box>
      </Box>
      <Box mb={6} maxW="sm" overflow="hidden">
        <Box p="6">
          <Skeleton height="20px" my="10px" />
        </Box>
      </Box>
    </Stack>
  )
}

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => <div>Error: {error.message}</div>

export const Success = ({ cupcakes }) => {
  return cupcakes.map((cupcake) => (
    <Cupcake key={cupcake.id} cupcake={cupcake}></Cupcake>
  ))
}

Or, if I wanted to call another GraphQL endpoint, in this case a PG-backed view served by Hasura for “daily story counts” my SDL is:

import gql from 'graphql-tag'

export const schema = gql`
  type Calendar {
    day: String!
    value: Int!
  }
  type Query {
    dailyStoryCounts: [Calendar!]!
  }
`

I then have a Hasura client (just like a lib/db)

import { GraphQLClient } from 'graphql-request'

export const request = async (
  query = {},
  domain = process.env.HASURA_DOMAIN
) => {
  const endpoint = `https://${domain}/v1/graphql`

  const graphQLClient = new GraphQLClient(endpoint, {
    headers: {
      'x-hasura-admin-secret': process.env.HASURA_KEY,
    },
  })

  try {
    return await graphQLClient.request(query)
  } catch (error) {
    console.log(error)
    return error
  }
}

Then my service uses that client to fetch:

import { request } from 'src/lib/hasuraClient'

export const dailyStoryCounts = async () => {
  const query = `
  {
    dailyStoryCounts: calendar {
      day: date
      value
    }
  }
 `
  const data = await request(query, process.env.HASURA_DOMAIN)

  return data['dailyStoryCounts']
}

and my cells is normal and can render a nice calendar chart:

import { Box } from '@chakra-ui/core'
import { ResponsiveCalendar } from '@nivo/calendar'

export const QUERY = gql`
  query {
    dailyStoryCounts {
      day
      value
    }
  }
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => <div>Error: {error.message}</div>

export const Success = ({ dailyStoryCounts }) => {
  const firstDay = dailyStoryCounts[0].day
  const lastDay = dailyStoryCounts[dailyStoryCounts.length - 1].day

  return (
    <Box height="600px" width="100%">
      <ResponsiveCalendar
        data={dailyStoryCounts}
        from={firstDay}
        to={lastDay}
        emptyColor="#eeeeee"
        colors={['#61cdbb', '#97e3d5', '#e8c1a0', '#f47560']}
        margin={{ top: 40, right: 40, bottom: 40, left: 40 }}
        yearSpacing={40}
        monthBorderColor="#ffffff"
        dayBorderWidth={2}
        dayBorderColor="#ffffff"
        legends={[
          {
            anchor: 'bottom-right',
            direction: 'row',
            translateY: 36,
            itemCount: 4,
            itemWidth: 42,
            itemHeight: 36,
            itemsSpacing: 14,
            itemDirection: 'right-to-left',
          },
        ]}
      />
    </Box>
  )
}

(note: I turned off data in July)

Ok. That was long winded, I know.

But, to implement Fauna right now I would either:

  • use the Fauna client and write FQL (ie, the Contentful SDK approach)
  • make a gql client and write graphql against Fauna (the Hasura approach)

What I don’t think I would want is for the web to call Fauna directly or RW’s graphql to somehow relay the gql to Fauna. Do I? Don’t think so, but I have not experimented.

But what I think is super super cool is that I think if I had services that talked to Contentful (cupcakes) and Hasura (stories) and Prisma/Postgres (users), I am pretty sure I could make a single graphql request from a cell like

   query {
     cupcakes {
       id
       name
     }
 
    stories {
     id
     title
    }

    users {
      id 
      email
    }
}

And I could render all the data from three different sources in a Dashboard cell like:

export const Success = ({ cupcakes, stories, users }) => {
}

And if the id’s somehow matched, I could probably even filter: give me stories this user wrote about these cupcakes.

That’s really really cool.

BTW - this is Monty: “Monty likes to play in the woods to hide from his sister Minty.”

1 Like

Awesome, that was very, very helpful, and definitely not long winded. My goal here is to create a tutorial for Fauna to get Fauna users up and running with Redwood as easily as possible. So I think the FQL approach is more appealing to me here since we’ll be more on Fauna’s turf so to speak.

I’d want to explore the gql client approach as well because that would be more agnostic. As David was saying we’ll want to figure out Mongo and other DBs, and since Fauna is a document store there’s cross-over there, but the query languages are totally different.

1 Like

Same here.

FQL is really strange (least to me coming from SQL) at first, but I got used to it for basic queries. Figuring out the equivalent of joins still makes my head hurt a bit.

Plus (I think) to support GraphQL in Fauna one has to create a schema – so that would not be the “up and running with Redwood as easily as possible” case.

That reminds me – looking at some of the FQL I did – pagination. I need to explore how to do that in RW.

Yeah I’m always hesitant to invest in learning any DSL but it’s hard to avoid with databases, even those that follow the SQL spec end up with slightly different implementations since the spec is ambiguous.

Thankfully Fauna has really invested in their docs and blog articles, I’m going through this series right now to get up to speed. Not sure how deep you’ve gotten into their material but they’ve got a section on pagination that should be helpful.

Just a heads up, @thedavid, your link to prisma2’s supported database list in the first post of this Topic is a dead link and gives you a redirect to Prisma’s docs. If you click the “Edit this page on Github” link you’ll find the new place on Github the content lives.

Looks like they did a rearrangement cause they originally had a docs folder inside the prisma repo but wanted to have a dedicated docs repo by itself.

1 Like

Thanks for the heads up. Updated OP with new website page.

Wanted to add this here to complete the story since recently a guide: https://fauna.com/blog/building-a-minimum-viable-stack-with-redwoodjs-and-faunadb
came out on how to use RedwoodJS with FaunaDB. :heart_eyes:
The guide bypasses Prisma if I understood correctly.

2 Likes