├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── app.js ├── images ├── Matthew-Guay.jpg └── oskar-yildiz-gy08FXeM2L4-unsplash.jpg ├── index.js ├── package-lock.json ├── package.json ├── routes ├── controllers │ ├── deleteImage.js │ ├── imageUpload.js │ ├── persistImage.js │ ├── retrieveImage.js │ └── updateImage.js └── routes.js └── services ├── dbConnect.js └── tutor.sql /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # use docker node 10 2 | FROM node:10 3 | 4 | # create a directory to run docker 5 | WORKDIR /app 6 | 7 | # copy package.json into the new directory 8 | COPY package.json /app 9 | 10 | # install the dependencies 11 | RUN npm install 12 | 13 | # copy all other files into the app directory 14 | COPY . /app 15 | 16 | # open port 5000 17 | EXPOSE 5000 18 | 19 | # run the server 20 | CMD node index.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 NJOKU SAMSON EBERE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Server-tutorial 2 | This is a tutorial was to teach how to create a simple, secure and robust nodejs server but we have expanded our scope to cloudinary and postgres 3 | 4 | **Full details on how to build out this server is found [here](https://dev.to/ebereplenty/setting-up-a-simple-secure-and-robust-node-js-server-10n0)**. 5 | 6 | **Full details on how to upload images to cloudinary using nodejs is found [here](https://dev.to/ebereplenty/image-upload-to-cloudinary-with-nodejs-and-dotenv-4fen)**. 7 | 8 | **Full details on how to persist and retrieve images to cloudinary using nodejs and postgres is found [here](https://dev.to/ebereplenty/cloudinary-and-postgresql-persisting-and-retrieving-images-using-nodejs-31b2)**. 9 | 10 | **Full details on how to delete and update images to cloudinary using nodejs and postgres is found [here](https://t.co/XDR1BtlHNI?amp=1)**. 11 | 12 | **Full details on Nodejs Code Structure Optimization With Express Routing is found [here](https://t.co/vJvTCd2KdF?amp=1)**. 13 | 14 | ## Dependences 15 | - [Express](https://www.npmjs.com/package/express) 16 | - [Cloudinary](https://cloudinary.com/) 17 | - [Node](http://nodejs.org/) 18 | - [NPM](https://www.npmjs.com/) 19 | - [DotENV](https://www.npmjs.com/package/dotenv) 20 | - [Nodemon](https://www.npmjs.com/package/nodemon) 21 | - [Node Postgres](https://node-postgres.com/) 22 | 23 | 24 | ## SETTING UP 25 | - Fork this repository 26 | - Clone the repositury to your machine 27 | - Open up a terminal 28 | - Navigate into the project directory 29 | - Run npm install to install all needed dependencies 30 | - Run nodemon index to spin up the server 31 | - The server runs on port 3000 http://localhost:3000/ 32 | 33 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | // import the routes file 5 | const routes = require("./routes/routes") 6 | 7 | // body parser configuration 8 | const bodyParser = require("body-parser"); 9 | app.use(bodyParser.json()); 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | 12 | // register the routes 13 | app.use('/', routes); 14 | 15 | module.exports = app; 16 | -------------------------------------------------------------------------------- /images/Matthew-Guay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EBEREGIT/server-tutorial/48498c187f98793154f89b3a332809c3a6554a19/images/Matthew-Guay.jpg -------------------------------------------------------------------------------- /images/oskar-yildiz-gy08FXeM2L4-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EBEREGIT/server-tutorial/48498c187f98793154f89b3a332809c3a6554a19/images/oskar-yildiz-gy08FXeM2L4-unsplash.jpg -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const app = require('./app'); 3 | 4 | const normalizePort = val => { 5 | const port = parseInt(val, 10); 6 | 7 | if (isNaN(port)) { 8 | return val; 9 | } 10 | if (port >= 0) { 11 | return port; 12 | } 13 | return false; 14 | }; 15 | const port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | const errorHandler = error => { 19 | if (error.syscall !== 'listen') { 20 | throw error; 21 | } 22 | const address = server.address(); 23 | const bind = typeof address === 'string' ? 'pipe ' + address : 'port: ' + port; 24 | switch (error.code) { 25 | case 'EACCES': 26 | console.error(bind + ' requires elevated privileges.'); 27 | process.exit(1); 28 | break; 29 | case 'EADDRINUSE': 30 | console.error(bind + ' is already in use.'); 31 | process.exit(1); 32 | break; 33 | default: 34 | throw error; 35 | } 36 | }; 37 | 38 | const server = http.createServer(app); 39 | 40 | server.on('error', errorHandler); 41 | server.on('listening', () => { 42 | const address = server.address(); 43 | const bind = typeof address === 'string' ? 'pipe ' + address : 'port ' + port; 44 | console.log('Listening on ' + bind); 45 | }); 46 | 47 | server.listen(port); 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "create": "node ./services/dbConnect createTables" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "cloudinary": "^1.19.0", 14 | "dotenv": "^8.2.0", 15 | "express": "^4.17.1", 16 | "heroku": "^7.41.1", 17 | "make-runnable": "^1.3.6", 18 | "pg": "^8.0.3" 19 | }, 20 | "devDependencies": { 21 | "nodemon": "^2.0.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /routes/controllers/deleteImage.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary").v2; 2 | require("dotenv").config(); 3 | const db = require("../../services/dbConnect"); 4 | 5 | // cloudinary configuration 6 | cloudinary.config({ 7 | cloud_name: process.env.CLOUD_NAME, 8 | api_key: process.env.API_KEY, 9 | api_secret: process.env.API_SECRET, 10 | }); 11 | 12 | exports.deleteImage = (request, response) => { 13 | // unique ID 14 | const { cloudinary_id } = request.params; 15 | 16 | // delete image from cloudinary first 17 | cloudinary.uploader 18 | .destroy(cloudinary_id) 19 | 20 | // delete image record from postgres also 21 | .then(() => { 22 | db.pool.connect((err, client) => { 23 | // delete query 24 | const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1"; 25 | const deleteValue = [cloudinary_id]; 26 | 27 | // execute delete query 28 | client 29 | .query(deleteQuery, deleteValue) 30 | .then((deleteResult) => { 31 | response.status(200).send({ 32 | message: "Image Deleted Successfully!", 33 | deleteResult, 34 | }); 35 | }) 36 | .catch((e) => { 37 | response.status(500).send({ 38 | message: "Image Couldn't be Deleted!", 39 | e, 40 | }); 41 | }); 42 | }); 43 | }) 44 | .catch((error) => { 45 | response.status(500).send({ 46 | message: "Failure", 47 | error, 48 | }); 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /routes/controllers/imageUpload.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary").v2; 2 | require("dotenv").config(); 3 | 4 | // cloudinary configuration 5 | cloudinary.config({ 6 | cloud_name: process.env.CLOUD_NAME, 7 | api_key: process.env.API_KEY, 8 | api_secret: process.env.API_SECRET, 9 | }); 10 | 11 | exports.imageUpload = (request, response) => { 12 | // collected image from a user 13 | const data = { 14 | image: request.body.image, 15 | }; 16 | 17 | // upload image here 18 | cloudinary.uploader 19 | .upload(data.image) 20 | .then((result) => { 21 | response.status(200).send({ 22 | message: "success", 23 | result, 24 | }); 25 | }) 26 | .catch((error) => { 27 | response.status(500).send({ 28 | message: "failure", 29 | error, 30 | }); 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /routes/controllers/persistImage.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary").v2; 2 | require("dotenv").config(); 3 | const db = require("../../services/dbConnect"); 4 | 5 | // cloudinary configuration 6 | cloudinary.config({ 7 | cloud_name: process.env.CLOUD_NAME, 8 | api_key: process.env.API_KEY, 9 | api_secret: process.env.API_SECRET, 10 | }); 11 | 12 | exports.persistImage = (request, response) => { 13 | // collected image from a user 14 | const data = { 15 | title: request.body.title, 16 | image: request.body.image, 17 | }; 18 | 19 | // upload image here 20 | cloudinary.uploader 21 | .upload(data.image) 22 | .then((image) => { 23 | db.pool.connect((err, client) => { 24 | // inset query to run if the upload to cloudinary is successful 25 | const insertQuery = 26 | "INSERT INTO images (title, cloudinary_id, image_url) VALUES($1,$2,$3) RETURNING *"; 27 | const values = [data.title, image.public_id, image.secure_url]; 28 | 29 | // execute query 30 | client 31 | .query(insertQuery, values) 32 | .then((result) => { 33 | result = result.rows[0]; 34 | 35 | // send success response 36 | response.status(201).send({ 37 | status: "success", 38 | data: { 39 | message: "Image Uploaded Successfully", 40 | title: result.title, 41 | cloudinary_id: result.cloudinary_id, 42 | image_url: result.image_url, 43 | }, 44 | }); 45 | }) 46 | .catch((e) => { 47 | response.status(500).send({ 48 | message: "failure", 49 | e, 50 | }); 51 | }); 52 | }); 53 | }) 54 | .catch((error) => { 55 | response.status(500).send({ 56 | message: "failure", 57 | error, 58 | }); 59 | }); 60 | } -------------------------------------------------------------------------------- /routes/controllers/retrieveImage.js: -------------------------------------------------------------------------------- 1 | const db = require("../../services/dbConnect"); 2 | 3 | exports.retrieveImage = (request, response) => { 4 | // data from user 5 | const { cloudinary_id } = request.params; 6 | 7 | db.pool.connect((err, client) => { 8 | // query to find image 9 | const retrieveQuery = "SELECT * FROM images WHERE cloudinary_id = $1"; 10 | const value = [cloudinary_id]; 11 | 12 | // execute query 13 | client 14 | .query(retrieveQuery, value) 15 | .then((output) => { 16 | response.status(200).send({ 17 | status: "success", 18 | data: { 19 | message: "Image Retrieved Successfully!", 20 | id: output.rows[0].cloudinary_id, 21 | title: output.rows[0].title, 22 | url: output.rows[0].image_url, 23 | }, 24 | }); 25 | }) 26 | .catch((error) => { 27 | response.status(401).send({ 28 | status: "failure", 29 | data: { 30 | message: "could not retrieve record!", 31 | error, 32 | }, 33 | }); 34 | }); 35 | }); 36 | } -------------------------------------------------------------------------------- /routes/controllers/updateImage.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary").v2; 2 | require("dotenv").config(); 3 | const db = require("../../services/dbConnect"); 4 | 5 | // cloudinary configuration 6 | cloudinary.config({ 7 | cloud_name: process.env.CLOUD_NAME, 8 | api_key: process.env.API_KEY, 9 | api_secret: process.env.API_SECRET, 10 | }); 11 | 12 | exports.updateImage = (request, response) => { 13 | // unique ID 14 | const { cloudinary_id } = request.params; 15 | 16 | // collected image from a user 17 | const data = { 18 | title: request.body.title, 19 | image: request.body.image, 20 | }; 21 | 22 | // delete image from cloudinary first 23 | cloudinary.uploader 24 | .destroy(cloudinary_id) 25 | 26 | // upload image here 27 | .then(() => { 28 | cloudinary.uploader 29 | .upload(data.image) 30 | 31 | // update the database here 32 | .then((result) => { 33 | db.pool.connect((err, client) => { 34 | // update query 35 | const updateQuery = 36 | "UPDATE images SET title = $1, cloudinary_id = $2, image_url = $3 WHERE cloudinary_id = $4"; 37 | const value = [ 38 | data.title, 39 | result.public_id, 40 | result.secure_url, 41 | cloudinary_id, 42 | ]; 43 | 44 | // execute query 45 | client 46 | .query(updateQuery, value) 47 | .then(() => { 48 | // send success response 49 | response.status(201).send({ 50 | status: "success", 51 | data: { 52 | message: "Image Updated Successfully", 53 | }, 54 | }); 55 | }) 56 | .catch((e) => { 57 | response.status(500).send({ 58 | message: "Update Failed", 59 | e, 60 | }); 61 | }); 62 | }); 63 | }) 64 | .catch((err) => { 65 | response.status(500).send({ 66 | message: "failed", 67 | err, 68 | }); 69 | }); 70 | }) 71 | .catch((error) => { 72 | response.status(500).send({ 73 | message: "failed", 74 | error, 75 | }); 76 | }); 77 | } -------------------------------------------------------------------------------- /routes/routes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const imageUpload = require("./controllers/imageUpload"); 4 | const persistImage = require("./controllers/persistImage"); 5 | const retrieveImage = require("./controllers/retrieveImage"); 6 | const updateImage = require("./controllers/updateImage"); 7 | const deleteImage = require("./controllers/deleteImage"); 8 | 9 | router.get("/", (request, response, next) => { 10 | response.json({ message: "Hey! This is your server response!" }); 11 | next(); 12 | }); 13 | 14 | // image upload API 15 | router.post("/image-upload", imageUpload.imageUpload); 16 | 17 | // persist image 18 | router.post("/persist-image", persistImage.persistImage); 19 | 20 | // retrieve image 21 | router.get("/retrieve-image/:cloudinary_id", retrieveImage.retrieveImage); 22 | 23 | // delete image 24 | router.delete("/delete-image/:cloudinary_id", deleteImage.deleteImage); 25 | 26 | // update image 27 | router.put("/update-image/:cloudinary_id", updateImage.updateImage); 28 | 29 | module.exports = router; 30 | -------------------------------------------------------------------------------- /services/dbConnect.js: -------------------------------------------------------------------------------- 1 | const pg = require('pg'); 2 | require('dotenv').config(); 3 | 4 | // set production variable. This will be called when deployed to a live host 5 | const isProduction = process.env.NODE_ENV === 'production'; 6 | 7 | // configuration details 8 | const connectionString = `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`; 9 | 10 | // if project has been deployed, connect with the host's DATABASE_URL 11 | // else connect with the local DATABASE_URL 12 | const pool = new pg.Pool({ 13 | connectionString: isProduction ? process.env.DATABASE_URL : connectionString, 14 | ssl: isProduction, 15 | }); 16 | 17 | // display message on success message if successful 18 | pool.on('connect', () => { 19 | console.log('Teamwork Database connected successfully!'); 20 | }); 21 | 22 | 23 | const createTables = () => { 24 | const imageTable = `CREATE TABLE IF NOT EXISTS 25 | images( 26 | id SERIAL PRIMARY KEY, 27 | title VARCHAR(128) NOT NULL, 28 | cloudinary_id VARCHAR(128) NOT NULL, 29 | image_url VARCHAR(128) NOT NULL 30 | )`; 31 | pool 32 | .query(imageTable) 33 | .then((res) => { 34 | console.log(res); 35 | pool.end(); 36 | }) 37 | .catch((err) => { 38 | console.log(err); 39 | pool.end(); 40 | }); 41 | }; 42 | 43 | pool.on("remove", () => { 44 | console.log("client removed"); 45 | process.exit(0); 46 | }); 47 | 48 | //export pool and createTables to be accessible from an where within the application 49 | module.exports = { 50 | createTables, 51 | pool, 52 | }; 53 | 54 | require("make-runnable"); 55 | -------------------------------------------------------------------------------- /services/tutor.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 12.1 6 | -- Dumped by pg_dump version 12.1 7 | 8 | -- Started on 2020-05-26 11:02:31 9 | 10 | SET statement_timeout = 0; 11 | SET lock_timeout = 0; 12 | SET idle_in_transaction_session_timeout = 0; 13 | SET client_encoding = 'UTF8'; 14 | SET standard_conforming_strings = on; 15 | SELECT pg_catalog.set_config('search_path', '', false); 16 | SET check_function_bodies = false; 17 | SET xmloption = content; 18 | SET client_min_messages = warning; 19 | SET row_security = off; 20 | 21 | SET default_tablespace = ''; 22 | 23 | SET default_table_access_method = heap; 24 | 25 | -- 26 | -- TOC entry 203 (class 1259 OID 49321) 27 | -- Name: images; Type: TABLE; Schema: public; Owner: tutorial 28 | -- 29 | 30 | CREATE TABLE public.images ( 31 | id integer NOT NULL, 32 | title character varying(128) NOT NULL, 33 | cloudinary_id character varying(128) NOT NULL, 34 | image_url character varying(128) NOT NULL 35 | ); 36 | 37 | 38 | ALTER TABLE public.images OWNER TO tutorial; 39 | 40 | -- 41 | -- TOC entry 202 (class 1259 OID 49319) 42 | -- Name: images_id_seq; Type: SEQUENCE; Schema: public; Owner: tutorial 43 | -- 44 | 45 | CREATE SEQUENCE public.images_id_seq 46 | AS integer 47 | START WITH 1 48 | INCREMENT BY 1 49 | NO MINVALUE 50 | NO MAXVALUE 51 | CACHE 1; 52 | 53 | 54 | ALTER TABLE public.images_id_seq OWNER TO tutorial; 55 | 56 | -- 57 | -- TOC entry 2823 (class 0 OID 0) 58 | -- Dependencies: 202 59 | -- Name: images_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tutorial 60 | -- 61 | 62 | ALTER SEQUENCE public.images_id_seq OWNED BY public.images.id; 63 | 64 | 65 | -- 66 | -- TOC entry 2687 (class 2604 OID 49324) 67 | -- Name: images id; Type: DEFAULT; Schema: public; Owner: tutorial 68 | -- 69 | 70 | ALTER TABLE ONLY public.images ALTER COLUMN id SET DEFAULT nextval('public.images_id_seq'::regclass); 71 | 72 | 73 | -- 74 | -- TOC entry 2817 (class 0 OID 49321) 75 | -- Dependencies: 203 76 | -- Data for Name: images; Type: TABLE DATA; Schema: public; Owner: tutorial 77 | -- 78 | 79 | COPY public.images (id, title, cloudinary_id, image_url) FROM stdin; 80 | 8 Heroku Image ywdrgacv79cg18ap0w7l https://res.cloudinary.com/dunksyqjj/image/upload/v1590431624/ywdrgacv79cg18ap0w7l.jpg 81 | \. 82 | 83 | 84 | -- 85 | -- TOC entry 2824 (class 0 OID 0) 86 | -- Dependencies: 202 87 | -- Name: images_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tutorial 88 | -- 89 | 90 | SELECT pg_catalog.setval('public.images_id_seq', 8, true); 91 | 92 | 93 | -- 94 | -- TOC entry 2689 (class 2606 OID 49326) 95 | -- Name: images images_pkey; Type: CONSTRAINT; Schema: public; Owner: tutorial 96 | -- 97 | 98 | ALTER TABLE ONLY public.images 99 | ADD CONSTRAINT images_pkey PRIMARY KEY (id); 100 | 101 | 102 | -- Completed on 2020-05-26 11:02:33 103 | 104 | -- 105 | -- PostgreSQL database dump complete 106 | -- 107 | 108 | --------------------------------------------------------------------------------