Spring Boot ve React ile Url Kısaltıcı Yapımı

Niyazi Ekinci
11 min readFeb 8, 2021

--

Selamlar,

Bugün Spring Boot, React ve Nginx kullanarak URL kısaltıcı yapımına değineceğiz.

Öncelikle uygulamanın temel yapısından bahsetmek istiyorum. Uygulamamız bilinen URL kısaltıcılarda olduğu gibi, kaydedilmek istenen uzun bir url’i daha kısa bir url ile eşleştiren, kısa url’e erişilmek istendiğinde ise uzun url’e yönlendirme yapılmasını sağlayan bir uygulamadır.

Bu nedenle uygulamamızda url’leri saklayabilmek için Spring Boot ile servis yazacağız. Projeye başlamadan önce projenin çalışır halini görmek isterseniz bu adresi ziyaret edebilirsiniz.

Servisimizi yazalım

Öncelikle maven projesi oluşturmakla başlayalım. Maven projemizi oluşturduğumuzda bağımlılıkların tam ve eksiksiz yüklenmesi için aşağıdaki pom.xml dosyasını pom.xml dosyamızla değiştirelim.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.app</groupId>
<artifactId>urlshortener</artifactId>
<version>0.0.1</version>
<name>urlshortener</name>
<description>URL Shortener</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
<start-class>com.app.urlshortener.UrlShortenerApplication</start-class>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>

</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>

</project>

Bu değişikliği yaptıktan sonra komut satırından mvn clean install yaparak bağımlılıkların yüklenmesini sağlayalım. Daha sonra com.app.urlshortener paketinin altına Spring Boot main class’ımızı oluşturalım. Aşağıdaki gibi görünmesi gerekiyor;

package com.app.urlshortener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UrlShortenerApplication {

public static void main(String[] args) {
SpringApplication.run(UrlShortenerApplication.class, args);
}
}

Bu class ile birlikte Spring Boot uygulamamızın başlangıcı sağlanacaktır. Daha sonra url’lerimizi veritabanında modellemek ve maplemek için Link sınıfımızı oluşturalım. com.app.urlshortener.model paketinin altına Link adında bir sınıf oluşturalım ve aşağıdaki field’ları class’ımıza ekleyelim.

@Data
public class Link {
@Id
private String id;
private String url;
private String shortUrl;
@CreatedDate
private Date createDate;
}

@Data annotationuyla birlikte pom.xml dosyasında belirtmiş olduğumuz Lombok kütüphanesinin özelliklerinden faydalanıyoruz @Data annotation’u bize field’ların getter ve setterlar’ının otomatik üretilmesini sağlıyor. Link modelimizi yazdığımıza göre Spring Data JPA MongoDB kütüphanesinin soyutlamasından yararlanarak veritabanı repository katmanını oluşturalım. Ama öncelikle veritabanı bağlantı stringimizi resources altında application.yml dosyasına ekleyelim.

server:
port: 6060
spring:
application:
name: urlshortener-service
data:
mongodb:
uri: ************************

uri için kendi veritabanı bağlantı stringimizi yazalım. Ardından repository katmanından devam edelim. com.app.urlshortener.repository paketinin altına LinkRepository interface’imizi oluşturalım.

@Repository
public interface LinkRepository extends MongoRepository<Link, String> {
Link findByShortUrl(String shortUrl);
boolean existsByShortUrl(String shortUrl);
}

@Repository annotation’uyla işaretlediğimiz LinkRepository interface’imiz MongoRepository<Link, String> interface’ini extend etmektedir. Bu sayede spring boot bizim için interface’imizi concrete ederek inject edecektir. Burada interface üzerinde tanımladığımız querylerde yine bizim için oluşturulacaktır. Örneğin, findByShortUrl bize shortUrl’e göre veritabanında eğer varsa veri getirecektir.

Daha sonra bu repository’i kullanacağımız servis katmanını kodlamaya geçelim. Öncelikle com.app.urlshortener.service paketinin içine LinkService class’ımızı oluşturalım.

@Service
public class LinkService {
}

Sınıfımızı oluşturduk, @Service annotationu Spring Boot’a bu class’ın dependency injection için kullanılabileceğini söylüyor Yani bir üst katmanda @Autowired yazarak erişebilmemizi sağlıyor.

Burada az önce oluşturmuş olduğumuz repository interface’ini inject edelim.

@Autowired private LinkRepository repository;

Inject ettikten sonra ilk başta uzun url’i kısa url’e çeviren generate metodumuzu yazalım.

public String generate(String url){
if (!url.startsWith("http")){
url = "http://" + url;
}
String shortUrl = generateHelper(url);
Link link = new Link();
link.setCreateDate(new Date());
link.setId(UUID.randomUUID().toString());
link.setShortUrl(shortUrl);
link.setUrl(url);
repository.save(link);
return shortUrl;
}

Url’miz eğer http ile başlamıyor ise url’in başına http ekleyerek tam bir link olmasını sağlıyoruz ki http yönlendirmesi yaptığımızda doğru sonucu elde edelim. Daha sonra generateHelper isimli metodumuzu çağırıyoruz. Generate helper bize 6 harften oluşan abcdefghjklmnoprstuvyzwx1234567890 karakterlerini içeren rasgele karakter dizisi üretmek için yardım ediyor. Stringimizi ürettikten sonra veritabanına kaydediyoruz.

private String generateHelper(String url){
String geneatedUrl = RandomStringUtils.random(6, "abcdefghjklmnoprstuvyzwx1234567890");
boolean exists = repository.existsByShortUrl(geneatedUrl);
if (exists){
return generateHelper(url);
}
return geneatedUrl;
}

Daha sonra kısa url ile sistemimize istek yapıldığında uzun url’e yönlendirme yapmak için gerekli servis metodunu yazıyoruz.

public void get(HttpServletResponse response, String url){
Link get = repository.findByShortUrl(url);
if (Objects.nonNull(get)) {
response.setStatus(301);
response.addHeader("Location", get.getUrl());
return;
}
response.setStatus(301);
response.addHeader("Location", "404");
}

Burada servisimize bir üst katmandan gelen HttpServletResponse nesnesi bize http response’una müdahale etmeyi sağlamış oluyor. Url ise veritabanında sorgulayacağımız kısaltılmış url’e karşılık gelmektedir. Veritabanına istek attıktan sonra eğer sonuç döner ise HTTP 301 Kalıcı Yönlendirme ile kullanıcıyı uzun url’e yönlendirmiş olacağız.

response.setStatus(301) ile http durum kodunu belirledik, response.addHeader(“Location”, get.getUrl()) ile de yönlendirme yapmak istediğimiz URL’i verdik. Eğer veritabanından veri dönmezse 404 sayfasına yönlendirme yaptırdık. Böylelikle 301 kodunu ve Location olarak belirtilmiş URL’i alan tarayıcılar belirtilen url’e yönlendirme yapacaklardır.

Daha sonra dış uygulamalarla aramızdaki sözleşme olan API katmanını yazmaya başlayalım. Uygulamamız oldukça basit bir uygulama olduğu için 2 tane endpointi olan bir api sınıfı oluşturalım. com.app.urlshortener.api paketi altına LinkApi adında bir sınıf oluşturalım.

@RestController
@RequestMapping("/urlshortener")
@CrossOrigin
public class LinkApi {
}

HTTP isteklerini karşılayacağımız katman class’ını oluşturduktan sonra endpointleri yazalım. Öncelikle url’imizi kısaltacağımız endpoint’i yazalım. Fakat burada endpointi yazmadan önce az önce yazdığımız servis katmanını sınıfımıza inject edelim.

@Autowired private LinkService service;

@Autowired annotation’uyla işaretlediğimiz LinkService class’ını Spring Boot bize kullanabilmemiz için inject edecektir. Şimdi kaldığımız yerden devam edelim http post metodu ve /urlshortener/generate path’iyle gelen isteklerin yeni URL oluşturmasını istiyoruz bu nedenle aşağıdaki metodu yazmamız gerekiyor.

@PostMapping("/generate")
public String generate(@RequestBody String url){
return service.generate(url);
}

Request Body’sine string şeklinde yazılan URL’in geriye string döndürmesi bekleniyor bir alt katmanda bu sözleşmeyi yerine getiren kodumuzu az önce yazdık şimdi buradan o metodu kullanarak client’e cevap dönüyoruz.

Son endpoint olan, generate edilen url’e yönlendiren, metodumuzu yazalım. Http get ve /urlshortener/{url} şeklinde gelen isteklerin gerçek url’e yönlendirme yapmasını istiyoruz.

@GetMapping("/{url}")
public void get(HttpServletResponse response, @PathVariable("url") String url){
service.get(response, url);
}

Burada HttpServletResponse nesnesinin spring tarafından inject edilmesini istedik çünkü response üzerinden varsa gerçek url’e HTTP 301 kalıcı yönlendirmesi yapacağız. Sınıfımızın son hali aşağıdaki gibidir.

@RestController
@RequestMapping("/urlshortener")
@CrossOrigin
public class LinkApi {

@Autowired private LinkService service;

@PostMapping("/generate")
public String generate(@RequestBody String url){
return service.generate(url);
}

@GetMapping("/{url}")
public void get(HttpServletResponse response, @PathVariable("url") String url){
service.get(response, url);
}
}

Servis katmanında yazdığımız kodumuzu api katmanından dış dünyaya açtığımıza göre şimdi önyüz kodlamasına geçelim.

Önyüz kodlaması

Önyüzde React kütüphanesini kullanacağımız için öncelikle npm ve node.js’in kurulu olduğundan emin olduktan sonra npx create-react-app frontendkomutunu çalıştıralım. Daha sonra ise react-material kütüphanesini yükleyelim.

npm i @material-ui/core --save
npm i @material-ui/icons --save

Ardından yine bağımlılığımız olan react-router-dom kütüphanesini kuralım.

npm i react-router-dom --save

Daha sonra create-react-app ile gelen App.js dosyamızın içini temizleyelim ve react-router-dom kütüphanesinden gelen router’imizi import edelim. Böylelikle routing işlemlerimizi yapabilmiş olacağız.

import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";

Import ettikten sonra App.js dosyamızı aşağıdaki gibi dolduralım.

const App = () => {

return (
<Router>
<Switch>
<Route exact path="/">
<IndexPage/>
</Route>
<Route exact path="/404">
<div style={{display: 'flex', justifyContent: 'center', alignItems:'center', height: '100vh', fontSize: '24px'}}>404 - Not Found</div>
</Route>
</Switch>
</Router>
);
}

export default App;

Router ve Switch componentleri altına yazmış olduğumuz route’lar her biri bir sayfa olmak üzere url’lerimizi tanımlamaktadır. exact props’u tam olarak eşleşme sağlandığında route içindeki component’in çalıştırılması gerektiğini belirtmektedir. Burada IndexPage adlı bir tane daha componentimiz var bu component’i şimdi kodlamaya başlayacağız.

src klasörünün altına pages adında bir klasör oluşturalım ve içinde index.js adlı bir dosya oluşturalım ardından index.styles.js adında bir dosya daha oluşturalım style’ları react-material’ın sağlamış olduğu api üzerinden yazacağız.

index.js dosyamızı açalım ardından aşağıdaki fonksiyonu yazalım.

const IndexPage = () => {

}
export default IndexPage;

Önce gerekli olan import işlemlerini yapalım.

import React from "react";
import {Container, Link, Typography} from "@material-ui/core";
import useStyles from "./index.styles";
import UrlShortener from "../components/url-shortener/url-shortener";

Buradaki UrlShortener’i az sonra kodlamaya başlayacağız. Şimdi componentimizin içini bu şekilde dolduralım;

const IndexPage = () => {
const classes = useStyles();
React.useEffect(() => {
document.title = "Url Shortener";
}, []);
return (
<Container className={classes.main} maxWidth="md">
<div className={classes.root}>
<Link href="/"><Typography color="secondary">URL Shortener</Typography></Link>
</div>
<div className={classes.container}>
<Typography variant="h2">URL Shortener</Typography>
<Typography color="primary">Nice URL, shorten long URLs.</Typography>
<UrlShortener />
</div>
</Container>
)
}

Yukarıdaki kodu açıklamak gerekirse index.styles.js dosyasında yazmış olduğumuz css’i useStyles olarak componentimize import ettik ardından componentimizin içinde const classes = useStyles(); şeklinde kullanmaya hazır hale getirdik. Daha sonra React.useEffect içinde sayfamızın browserda görünecek olan başlığını ayarladık. Container componenti ile sayfamızı sarmaladık maxWidth=”md” props’una sahip olan componentimiz sayfanın içeriğini belirli bir boyuta getirip ortalayacaktır. Bu class’a index.styles.js içinde yazmış olduğumuz main css sınıfını vermiş olduk. Daha sonra üst menüye sahip olmak için bir altına div oluşturup sol üst tarafa yerleşecek olan Link’imizi verdik. Ardından iç container olarak nitelendirdiğimiz div’in içinde sitemizde görünecek olan başlıkları ve UrlShortener componentini çağırıyoruz.

import makeStyles from "@material-ui/core/styles/makeStyles";

const useStyles = makeStyles({
main: {
display: 'grid',
gridTemplateRows: 'auto 1fr',
height: '100vh'
},
root: {
width: '100%',
fontSize: 'large',
display: 'flex',
padding: '20px 0',
flex: '1 0 auto',
justifyContent: 'space-between'
},
container: {
display: 'flex',
flexDirection: 'column',
gap: '20px',
paddingTop: '120px'
},
h1: {
fontSize: '64px',
fontWeight: 'bolder'
}
});

export default useStyles;

index.js dosyasında kullanmış olduğumuz style class’ları yukarıdaki gibidir. Şimdi UrlShortener component’ini yazmaya başlayalım. Öncelikle src klasörünün altına components adında bir klasör oluşturalım daha sonra url-shortener adında bir klasör daha oluşturup içinde url-shortener.js ve url-shortener.styles.js adında dosyalar oluşturalım. url-shortener.js dosyamızı aşağıdaki gibi ilklendirelim.

const UrlShortener = () => {

const classes = useStyles();
}
export default UrlShortener;

Daha sonra React hookları kullanarak aşağıdaki state’lerimizi oluşturalım.

const [result, setResult] = React.useState(null);

Bu state’imiz servise göndermiş olduğumuz uzun url’in sonucunda dönen kısa url’i tutmak için kullanılmaktadır.

const [baseUrl, setBaseUrl] = React.useState('');

Bu state ise servise göndermiş olduğumuz uzun url’i tutmaktadır.

const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(false);

Bu iki state’den loading servise istek yapıldığında true olarak setlenecek ve ekranda loading iconu görüntülenmesini sağlayacaktır. Servisten response döndüğünde ise false olarak setlenecek ve sonucun görüntülenmesini sağlayacaktır. Error state’i ise hata olması durumunda ekranda hatayı basmak için kullanılmaktadır.

const [copied, setCopied] = React.useState(false);

Son olarak copied state’i ekranda çıkan url’i buton ile kopyaladıktan sonra copied uyarısının 1100 ms ekranda görünmesini sağlamak için kullanılacaktır.

const copyURL = () => {
const resultTextField = document.getElementById('result-textfield');
resultTextField.focus();
resultTextField.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Oops, unable to copy');
}
setCopy(true);
setTimeout(() => {
setCopy(false);
}, 1100);
}

Bu metod ile ekranda çıkan kopyalama butonuna bastığımızda result state’indeki değeri CTRL+V yapabilmek için alıyoruz.

const getResult = () => {
setResult(null);
setError(false);
setLoading(true);
fetch('generate', {
method: 'POST',
body: baseUrl
}).then(res => res.text()).then(res => {
const host = window.location.href;
setResult(host + res);
setLoading(false);
}).catch(err => {
setError('Error: Try again');
setLoading(false);
});
}

Bu metod ile ise az önce spring boot ile yazmış olduğumuz servise istekte bulunuyoruz. Eğer istek başarılı olursa dönen sonucu ekranda göstermek üzere result state’ine aktarıyoruz başarısız olursa error state’ini setliyoruz.

Daha sonra yukarıdaki kodu fonksiyondan return ediyoruz. Burada kısaca açıklamak gerekirse bir tane material-ui’ın sağlamış olduğu TextField componentimiz var ve bu componente yazdığımız url baseUrl state’ine setleniyor. Bir tane içeriği Get URL olan butonumuz var ve bu butona basıldığında baseUrl state’iyle birlikte spring boot servisimize istek atıyor ve sonucu getiriyor. Loading state’i eğer true olur ise loading iconu ekranda görünüyor aynı şekilde error state’i içinde geçerli null veya false olmadıkça bu değerleri ekranda görebiliyoruz. En sonda ise result state’i null olmadığı durumlarda bir tane değiştirilemez TextField componenti içinde servisten dönen cevabı göstermiş oluyoruz. Hemen yanında IconButton componenti ile eğer servisten dönen değerin kopyalanması isteniyorsa kopyalayabilmesini sağlıyoruz. IconButton’un onClick eventinde yukarıda yazmış olduğumz copyUrl fonksiyonun çağrımını sağladık.

import makeStyles from "@material-ui/core/styles/makeStyles";

const useStyles = makeStyles({
root: {
display: 'grid',
gridTemplateColumns: '1fr auto',
gap: '8px'
},
result: {
width: '100%',
padding: '20px',
boxSizing: 'border-box'
},
container: {
position: 'relative'
},
iconButton: {
position: 'absolute',
bottom: 0,
right: 0,
marginBottom: '12px',
marginRight: '4px'
},
loading: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
});

export default useStyles;

url-shortener.styles.js dosyasına yukarıdaki stillerimizi yazıyoruz. Bununla birlikte aslında projemizin kodlama kısmı bitmiş oluyor.

Reverse Proxy

Nginx kullanarak projemizin frontend ve backend kodlarını aynı alan adı altında tutmaya çalışacağız ve frontend’den backend’e proxy aracılığıyla istek atacağız. Nginx kullanırken ise nginx docker imageları vasıtasıyla bu işlemi yapacağız. Öncelikle proje solution klasörümüzün içine nginx adında bir klasör oluşturalım ve nginx.conf adında bir dosya oluşturalım. Dosyanın içine aşağıdaki ayarları oluşturalım.

server {
listen 80;
server_name 0.0.0.0;
location /404 {
proxy_pass http://frontend:3000/404/;
}

location ~ "^/([0-9a-zA-Z]{4,8})$" {
resolver 127.0.0.11;
proxy_pass http://app:6060/urlshortener/$1;
}
location / {
proxy_pass http://frontend:3000/;
}

}

Burada 80 portunda ve 0.0.0.0 server_name’i olarak uygulamamızı açmak istediğimizi belirtiyoruz. Ardından location /404 ile server’imiza gelen alanadimiz.com/404 gibi bir isteğin frontend uygulamamizdaki 404 sayfasına yönlendirme yapmasını istiyoruz. Daha sonra bir altındaki location ile alfabe harfleri ve 0 dan 9 a kadar olan rakamları içeren ve 4 ile 8 karakter sayısında olan gelen istekleri backend uygulamamıza yönlendiriyoruz. Örneğin alanadimiz.com/generate şeklinde bir istek aldığımızda backend uygulamamiza bu isteğin gitmesini sağlamış oluyoruz. Ardından yine location / satırı ile sadece alanadimiz.com/ gibi bir istek gelirse bu isteği frontend uygulamamiza yönlendirmesini sağlamış oluyoruz böylece uygulamamız bir reverse proxy arkasında çalışmaya başlamış oluyor.

Deploy edelim

Birlikte örnek bir deploy yapmak için Play With Docker sitesini kullanacağız bu site bize 4'er saatlik ve sınırlı kaynaklar (genelde işimizi görüyor) vererek docker öğrenme ve demo yapabilmemizi sağlıyor.

Öncelikle projemizi frontend, link-shortener ve nginx klasörleri olacak şekilde bir klasör altında toplayalım.

Klasör altında topladıktan sonra frontend klasörü içinde aşağıdaki Dockerfile’ı oluşturalım.


FROM node:13.12.0-alpine


WORKDIR /app


ENV PATH /app/node_modules/.bin:$PATH


COPY package.json ./
COPY package-lock.json ./
RUN npm install


COPY . ./


CMD ["npm", "start"]

Kısaca anlatmak gerekirse dosyalarımızı docker’a kopyalıyoruz ve npm install yaparak bağımlılıkları yüklemesini sağlıyoruz ardından npm start yaparak 3000 portunda çalışmasını sağlıyoruz. Daha sonra yine link-shortener klasörü altında Dockerfile oluşturuyoruz.

FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD

COPY pom.xml /build/
COPY src /build/src/

WORKDIR /build/
RUN mvn clean package spring-boot:repackage

FROM openjdk:8-jre-alpine

WORKDIR /app

COPY --from=MAVEN_BUILD /build/target /app/

EXPOSE 6060
ENTRYPOINT ["java", "-jar", "urlshortener-0.0.1.jar"]

Burada ilk başta pom xml dosyamızı build klasörüne taşıyoruz ve ardından src klasörümüzü build/src altına taşıyoruz çalışma alanımızı build dizini olarak ayarladıktan sonra mvn clean package spring-boot:repackage komutunun çalıştırılmasını sağlıyoruz sonra alt satırlarda buradan çıkan jar dosyasının çalıştırılmasını sağlıyoruz. Docker bütün bu işlemleri bizim yerimize yapacaktır.

FROM nginx:1.17.4-alpine

RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx/conf.d

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Servis ve frontend dockerfile’larında olduğu gibi nginx içinde nginx klasörü altında dockerfile’imizi yukarıdaki gibi yazıyoruz. Daha sonra ana klasörümüzün içinde docker-compose.yaml adında bir dosya oluşturuyoruz ve içeriğini aşağıdaki gibi dolduruyoruz.

version: '3'
services:
nginx:
build:
context: nginx
dockerfile: Dockerfile
ports:
- '80:80'
restart: always
expose:
- "80"
links:
- 'app:app'
- 'frontend:frontend'
app:
build:
context: link-shortener
dockerfile: Dockerfile
restart: 'on-failure:10'
environment:
MONGODB_URI: <your-connection-string>
frontend:
build:
context: frontend
dockerfile: Dockerfile
restart: 'on-failure:10'

Docker Compose dosyamızda sırasıyla servislerimizi yazıyoruz. Nginx servisi içinde 80 portunu dışarıya yine 80 portuyla açacağımızı belirtiyoruz. Ardından app ve frontend uygulamalarına uygulamamızı bağlıyoruz. Uygulamamızın nginx klasörü altındaki Dockerfile ile ayağa kalkmasını belirttikten sonra aynı şekilde app ve frontend servislerini de yazıyoruz. App servisinde diğerlerinden farklı olarak environment MONGODB_URI environmentini setliyoruz. Daha sonra spring boot uygulamasında resources/application.yml altında aşağıdaki değişikliği yapıyoruz.

spring:
data:
mongodb:
uri: ${MONGODB_URI}

Bu değişiklikleride yaptıktan sonra uygulamamız docker-compose ile birlikte çalışabilir hale gelecektir.

Kodların tamamına bu link üzerinden erişebilirsiniz.

Dockerfile’larımızı da yazdığımıza göre;

  • Play With Docker adresine girerek giriş yapıyoruz.
  • Giriş yaptıktan sonra ‘Start’ butonuna basıyoruz.
  • Karşımıza gelen ekranda sol taraftan Add New Instance butonuna basıyoruz.
  • Makinamız oluştuktan sonra karşımızda terminal ekranı görüntülenecektir. Burada aşağıdaki komutu çalıştırarak projeyi instance’a indiriyoruz.
git clone https://github.com/nekinci/url-shortener.git

Ardından aşağıdaki komutu çalıştırıyoruz,

cd url-shortener

Ve son olarak;

docker-compose up

Build tamamlandıktan sonra sayfanın üst kısmında 80 yazılı bir link çıkacaktır bu linke tıklayarak projeyi çalıştırabilirsiniz. Bilgisayarınızda docker yüklü ise aynı işlemleri yaparak bilgisayarınızda da test edebilirsiniz. Play With Docker’ın bize vermiş olduğu link tabii ki uzun olacaktır ama uygulamayı daha kısa bir domain adıyla deploy ettiğimizde amacına tam anlamıyla hizmet edecektir.

Projenin çalışır haliyle ilgili bir link bırakmak gerekirse;

niceurl.live/1l7n97 bu domain ile çalışırlığını test edebilirsiniz. Yine niceurl.live adresine giderek URL kısaltabilirsiniz.

--

--