Skip to main content

Consuming the Plaid API with React and Go

Reposted from Medium
Published onMar 25, 2023
Consuming the Plaid API with React and Go
·

A Modern Full Stack REST Application

a quickstart for setting up plaid server and react client - qweliant/BetterPlaidQuickstart

Landing page after login to Plaid

Creds from Plaid

quickstart repos exist for multiple languages. You can use your language of choice for this project since the client is backend agnostic

Backend

git clone https://github.com/plaid/quickstart.git


version: "3.4"

services:
  go:
    build:
      context: .
      dockerfile: ./go/Dockerfile
    ports: ["8000:8000"]
    env_file:
      - .env
docker-compose up -d --build 

FROM golang:1.12 AS build

WORKDIR /opt/src
COPY . .
WORKDIR /opt/src/go

RUN go get -d -v ./...
RUN go build -o quickstart

FROM gcr.io/distroless/base-debian10
#FROM gcr.io/distroless/base-debian10:debug
COPY --from=build /opt/src/go/quickstart /

EXPOSE 8000
ENTRYPOINT ["/quickstart"]
# Get your Plaid API keys from the dashboard: https://dashboard.plaid.com/account/keys
PLAID_CLIENT_ID=CLIENT ID
PLAID_SECRET=SANDBOX SECRET
# Use 'sandbox' to test with fake credentials in Plaid's Sandbox environment
# Use 'development' to test with real credentials while developing
# Use 'production' to go live with real users
PLAID_ENV=sandbox
# PLAID_PRODUCTS is a comma-separated list of products to use when
# initializing Link, e.g. PLAID_PRODUCTS=auth,transactions.
# see https://plaid.com/docs/api/tokens/#create-a-link_token for a complete list
PLAID_PRODUCTS=transactions
# PLAID_COUNTRY_CODES is a comma-separated list of countries to use when
# initializing Link, e.g. PLAID_COUNTRY_CODES=US,CA.
# see https://plaid.com/docs/api/tokens/#create-a-link_token for a complete list
PLAID_COUNTRY_CODES=US,CA
# Only required for Oauth:
# Set PLAID_REDIRECT_URI to 'http://localhost:8000/oauth-response.html'
# The OAuth redirect flow requires an endpoint on the developer's website
# that the bank website should redirect to. You will need to configure
# this redirect URI for your client ID through the Plaid developer dashboard
# at https://dashboard.plaid.com/team/api.
PLAID_REDIRECT_URI=http://localhost:80
Attaching to quickstart_go_1go_1 | 2021/01/21 18:05:14 Error loading .env file. Did you copy .env.example to .env and fill it out?

{"error": "Post /link/token/create: unsupported protocol scheme \"\""}

var (
	PLAID_CLIENT_ID                   = ""
	PLAID_SECRET                      = ""
	PLAID_ENV                         = ""
	PLAID_PRODUCTS                    = ""
	PLAID_COUNTRY_CODES               = ""
	PLAID_REDIRECT_URI                = ""
	APP_PORT                          = ""
	client              *plaid.Client = nil
)

var environments = map[string]plaid.Environment{
	"sandbox":     plaid.Sandbox,
	"development": plaid.Development,
	"production":  plaid.Production,
}

func init() {
	// load env vars from .env file
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file. Did you copy .env.example to .env and fill it out?")
	}

	// set constants from env
	PLAID_CLIENT_ID = os.Getenv("PLAID_CLIENT_ID")
	PLAID_SECRET = os.Getenv("PLAID_SECRET")
	PLAID_ENV = os.Getenv("PLAID_ENV")
	PLAID_PRODUCTS = os.Getenv("PLAID_PRODUCTS")
	PLAID_COUNTRY_CODES = os.Getenv("PLAID_COUNTRY_CODES")
	PLAID_REDIRECT_URI = os.Getenv("PLAID_REDIRECT_URI")
	APP_PORT = os.Getenv("APP_PORT")

	// set defaults
	if PLAID_PRODUCTS == "" {
		PLAID_PRODUCTS = "transactions"
	}
	if PLAID_COUNTRY_CODES == "" {
		PLAID_COUNTRY_CODES = "US"
	}
	if PLAID_ENV == "" {
		PLAID_ENV = "sandbox"
	}
	if APP_PORT == "" {
		APP_PORT = "8000"
	}
	if PLAID_CLIENT_ID == "" {
		log.Fatal("PLAID_CLIENT_ID is not set. Make sure to fill out the .env file")
	}
	if PLAID_SECRET == "" {
		log.Fatal("PLAID_SECRET is not set. Make sure to fill out the .env file")
	}

	// create Plaid client
	client, err = plaid.NewClient(plaid.ClientOptions{
		PLAID_CLIENT_ID,
		PLAID_SECRET,
		environments[PLAID_ENV],
		&http.Client{},
	})
	if err != nil {
		panic(fmt.Errorf("unexpected error while initializing plaid client %w", err))
	}
}
view rawserver.go hosted with ❤ by GitHub

var (
	// set constants from env
	PLAID_CLIENT_ID     = os.Getenv("PLAID_CLIENT_ID")
	PLAID_SECRET        = os.Getenv("PLAID_SECRET")
	PLAID_ENV           = os.Getenv("PLAID_ENV")
	PLAID_PRODUCTS      = os.Getenv("PLAID_PRODUCTS")
	PLAID_COUNTRY_CODES = os.Getenv("PLAID_COUNTRY_CODES")
	PLAID_REDIRECT_URI  = os.Getenv("PLAID_REDIRECT_URI")
	APP_PORT            = os.Getenv("APP_PORT")
)

var environments = map[string]plaid.Environment{
	"sandbox":     plaid.Sandbox,
	"development": plaid.Development,
	"production":  plaid.Production,
}

func init() {
	// set defaults
	if PLAID_PRODUCTS == "" {
		PLAID_PRODUCTS = "transactions"
	}
	if PLAID_COUNTRY_CODES == "" {
		PLAID_COUNTRY_CODES = "US"
	}
	if PLAID_ENV == "" {
		PLAID_ENV = "sandbox"
	}
	if APP_PORT == "" {
		APP_PORT = "8000"
	}
	if PLAID_CLIENT_ID == "" {
		log.Fatal("PLAID_CLIENT_ID is not set. Make sure to fill out the .env file")
	}
	if PLAID_SECRET == "" {
		log.Fatal("PLAID_SECRET is not set. Make sure to fill out the .env file")
	}
}

var client = func() *plaid.Client {
	client, err := plaid.NewClient(plaid.ClientOptions{
		PLAID_CLIENT_ID,
		PLAID_SECRET,
		environments[PLAID_ENV],
		&http.Client{},
	})
	if err != nil {
		panic(fmt.Errorf("unexpected error while initializing plaid client %w", err))
	}
	return client
}()
view rawserver.go hosted with ❤ by GitHub

func main() {
	r := gin.Default()
	mainPage := "../html/index.html"
	oauthPage := "../html/oauth-response.html"
	r.LoadHTMLFiles(mainPage, oauthPage)
	r.Static("/static", "../static")

	r.POST("/api/info", info)
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{})
	})
func main() {

	r := gin.Default()
	r.POST("/api/info", info)

React

version: "3.8"

services:
  go:
    env_file:
    - .env
    build:
      context: .
      dockerfile: ./go/Dockerfile
    ports: ["8000:8000"]
    restart: on-failure
  client:
    stdin_open: true
    env_file:
      - .env
    build:
      context: .
      dockerfile: ./plaid/Dockerfile
    ports:
      - 80:80
    restart: on-failure
# STAGE 1 - build the react app 
# set the base image to build from 
# This is the application image from which all other subsequent 
# applications run. Alpine Linux is a security-oriented, lightweight #(~5Mb) Linux distribution.
FROM node:alpine as build
# set working directory
# this is the working folder in the container from which the app.   # will be running from
WORKDIR /app
# add the node_modules folder to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# copy package.json file to /app directory for installation prep
COPY ./package.json /app/
# install dependencies
RUN yarn --silent
# copy everything to /app directory
COPY . /app
# build the app 
RUN yarn build
# STAGE 2 - build the final image using a nginx web server 
# distribution and copy the react build files
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
# needed this to make React Router work properly 
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf /etc/nginx/conf.d
# Expose port 80 for HTTP Traffic 
EXPOSE 80
# start the nginx web server
CMD ["nginx", "-g", "daemon off;"]
server {

  listen 80;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}
yarn add axios react-plaid-link

import React, { useState, useCallback, useEffect } from "react";
import { usePlaidLink } from "react-plaid-link";
import axios from "axios";
import qs from "qs";


const tokenURL = `http://localhost:8000/api/create_link_token`;
const sendTokenURL = `http://localhost:8000/api/set_access_token`;

const  Link = () => {
  const [data, setData] = useState("");
  
  const fetchToken = useCallback(async () => {
    const config = {
      method: "post",
      url: tokenURL,
    };
    const res = await axios(config);
    console.log(res)
    setData(res.data.link_token);
  }, []);

  useEffect(() => {
    fetchToken();
  }, [fetchToken]);

  const onSuccess = useCallback(async (token, metadata) => {

    // send token to server
    const config = {
      method: "post",
      url: sendTokenURL,
      data: qs.stringify({ public_token: token }),
      headers: { "content-type": "application/json" },
    };
    try {
      const response = await axios(config);
      console.log(response)
    } catch (error) {
      console.error(error);
    }

  }, []);

  const config = {
    token: data,
    onSuccess,
  };

  const { open, ready, err } = usePlaidLink(config);

  if (err) return <p>Error!</p>;

  return (
    <div>
      <button onClick={() => open()} disabled={!ready}>
        Connect a bank account
      </button>
    </div>
  );
}

export default Link;
view rawExampleLink.js hosted with ❤ by GitHub
Comments
0
comment
No comments here
Why not start the discussion?